From 6c2772d09338f044096240eeb01aa09d11ddda59 Mon Sep 17 00:00:00 2001 From: Ginger Bill Date: Thu, 3 Nov 2016 16:26:22 +0000 Subject: [PATCH] Scrap Virtual Machine and begin again I just didn't like the style of it. --- code/demo.odin | 7 +- src/checker/types.cpp | 26 +- src/main.cpp | 14 +- src/old_vm.cpp | 1305 +++++++++++++++++++++++++++++++++++++++++ src/ssa.cpp | 16 +- src/ssa_to_llvm.cpp | 2 +- src/vm.cpp | 1182 +------------------------------------ 7 files changed, 1358 insertions(+), 1194 deletions(-) create mode 100644 src/old_vm.cpp diff --git a/code/demo.odin b/code/demo.odin index 991650ca0..ee353c884 100644 --- a/code/demo.odin +++ b/code/demo.odin @@ -23,5 +23,10 @@ main :: proc() { } foo() - x := test("Hello") + x = test("Hello").count as i64 + xp := ^x + p := xp^ + + z := [..]i64{1, 2, 3, 4} + z[0] = p } diff --git a/src/checker/types.cpp b/src/checker/types.cpp index a44632ee5..071cd0fa3 100644 --- a/src/checker/types.cpp +++ b/src/checker/types.cpp @@ -482,6 +482,20 @@ b32 is_type_float(Type *t) { } return false; } +b32 is_type_f32(Type *t) { + t = base_type(t); + if (t->kind == Type_Basic) { + return t->Basic.kind == Basic_f32; + } + return false; +} +b32 is_type_f64(Type *t) { + t = base_type(t); + if (t->kind == Type_Basic) { + return t->Basic.kind == Basic_f64; + } + return false; +} b32 is_type_pointer(Type *t) { t = base_type(t); if (t->kind == Type_Basic) { @@ -1188,6 +1202,17 @@ i64 type_size_of(BaseTypeSizes s, gbAllocator allocator, Type *t) { return align_formula(size, align); } + case Type_Tuple: { + i64 count = t->Tuple.variable_count; + if (count == 0) { + return 0; + } + type_set_offsets(s, allocator, t); + i64 size = t->Tuple.offsets[count-1] + type_size_of(s, allocator, t->Tuple.variables[count-1]->type); + i64 align = type_align_of(s, allocator, t); + return align_formula(size, align); + } break; + case Type_Record: { switch (t->Record.kind) { case TypeRecord_Struct: { @@ -1196,7 +1221,6 @@ i64 type_size_of(BaseTypeSizes s, gbAllocator allocator, Type *t) { return 0; } type_set_offsets(s, allocator, t); - // TODO(bill): Is this how it should work? i64 size = t->Record.struct_offsets[count-1] + type_size_of(s, allocator, t->Record.fields[count-1]->type); i64 align = type_align_of(s, allocator, t); return align_formula(size, align); diff --git a/src/main.cpp b/src/main.cpp index 902c3db2c..7ff6b7eb7 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -166,15 +166,13 @@ int main(int argc, char **argv) { ssa_gen_tree(&ssa); -#if 1 - { - VirtualMachine vm = {}; - vm_init(&vm, &ssa.module); - defer (vm_destroy(&vm)); +#if 0 + VirtualMachine vm = {}; + vm_init(&vm, &ssa.module); + // defer (vm_destroy(&vm)); - Array args = {}; // Empty - vm_call_proc_by_name(&vm, make_string("main"), args); - } + Array main_args = {}; // Empty + vm_call_proc_by_name(&vm, make_string("main"), main_args); #endif { diff --git a/src/old_vm.cpp b/src/old_vm.cpp new file mode 100644 index 000000000..fbacc0697 --- /dev/null +++ b/src/old_vm.cpp @@ -0,0 +1,1305 @@ +// TODO(bill): COMPLETELY REWORK THIS ENTIRE INTERPRETER +#include "dyncall/include/dyncall.h" + +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" + Type *type; +}; + +vmValue vm_make_value_ptr(Type *type, void *ptr) { + GB_ASSERT(is_type_pointer(type)); + vmValue v = {}; + v.type = default_type(type); + v.val_ptr = ptr; + return v; +} +vmValue vm_make_value_int(Type *type, i64 i) { + GB_ASSERT(is_type_integer(type) || + is_type_boolean(type) || + is_type_enum(type)); + vmValue v = {}; + v.type = default_type(type); + v.val_int = i; + return v; +} +vmValue vm_make_value_f32(Type *type, f32 f) { + GB_ASSERT(is_type_f32(type)); + vmValue v = {}; + v.type = default_type(type); + v.val_f32 = f; + return v; +} +vmValue vm_make_value_f64(Type *type, f64 f) { + GB_ASSERT(is_type_f64(type)); + vmValue v = {}; + v.type = default_type(type); + v.val_f64 = f; + return v; +} +vmValue vm_make_value_comp(Type *type, gbAllocator allocator, isize count) { + GB_ASSERT(is_type_string(type) || + is_type_any (type) || + is_type_array (type) || + is_type_vector(type) || + is_type_slice (type) || + is_type_maybe (type) || + is_type_struct(type) || + is_type_union(type) || + is_type_raw_union(type) || + is_type_tuple (type) || + is_type_proc (type)); + vmValue v = {}; + v.type = default_type(type); + array_init_count(&v.val_comp, allocator, count); + return v; +} + + + + + + +struct vmFrame { + VirtualMachine * vm; + vmFrame * caller; + ssaProcedure * curr_proc; + ssaBlock * prev_block; + ssaBlock * curr_block; + i32 instr_index; // For the current block + + 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 * + Map const_compound_lits; // Key: ssaValue * + vmValue exit_value; +}; + +void vm_exec_instr (VirtualMachine *vm, ssaValue *value); +vmValue vm_operand_value(VirtualMachine *vm, ssaValue *value); +void vm_store (VirtualMachine *vm, void *dst, vmValue val, Type *type); +vmValue vm_load (VirtualMachine *vm, void *ptr, Type *type); +void vm_print_value (vmValue value, Type *type); + +void vm_jump_block(vmFrame *f, ssaBlock *target) { + f->prev_block = f->curr_block; + f->curr_block = target; + f->instr_index = 0; +} + + +vmFrame *vm_back_frame(VirtualMachine *vm) { + if (vm->frame_stack.count > 0) { + return &vm->frame_stack[vm->frame_stack.count-1]; + } + return NULL; +} + +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 index) { + return type_offset_of(vm->module->sizes, vm->heap_allocator, type, index); +} + + +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); + map_init(&vm->const_compound_lits, vm->heap_allocator); + + for_array(i, vm->module->values.entries) { + ssaValue *v = vm->module->values.entries[i].value; + switch (v->kind) { + case ssaValue_Global: { + Type *t = ssa_type(v); + GB_ASSERT(is_type_pointer(t)); + i64 size = vm_type_size_of(vm, t); + i64 align = vm_type_align_of(vm, t); + void *mem = gb_alloc_align(vm->heap_allocator, size, align); + if (v->Global.value != NULL && v->Global.value->kind == ssaValue_Constant) { + vm_store(vm, mem, vm_operand_value(vm, v->Global.value), type_deref(t)); + } + map_set(&vm->globals, hash_pointer(v), vm_make_value_ptr(t, mem)); + } break; + } + } + +} +void vm_destroy(VirtualMachine *vm) { + array_free(&vm->frame_stack); + map_destroy(&vm->globals); + map_destroy(&vm->const_compound_lits); + gb_arena_free(&vm->stack_arena); +} + + + + + + +void vm_set_value(vmFrame *f, ssaValue *v, vmValue val) { + if (v != NULL) { + GB_ASSERT(ssa_type(v) != NULL); + 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.prev_block = proc->blocks[0]; + 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_proc(VirtualMachine *vm, ssaProcedure *proc, Array values) { + Type *type = base_type(proc->type); + GB_ASSERT_MSG(type->Proc.param_count == values.count, + "Incorrect number of arguments passed into procedure call!\n" + "%.*s -> %td vs %td", + LIT(proc->name), + type->Proc.param_count, values.count); + Type *result_type = type->Proc.results; + if (result_type != NULL && + result_type->Tuple.variable_count == 1) { + result_type = result_type->Tuple.variables[0]->type; + } + + if (proc->body == NULL) { + // GB_PANIC("TODO(bill): external procedure"); + gb_printf_err("TODO(bill): external procedure: %.*s\n", LIT(proc->name)); + vmValue result = {}; + result.type = result_type; + return result; + } + + void *result_mem = NULL; + if (result_type != NULL) { + result_mem = gb_alloc_align(vm->stack_allocator, + vm_type_size_of(vm, result_type), + vm_type_align_of(vm, result_type)); + } + + 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 (type->Proc.result_count > 0) { + vmValue r = f->result; + + gb_printf("%.*s -> ", LIT(proc->name)); + vm_print_value(r, result_type); + gb_printf("\n"); + + vm_store(vm, result_mem, r, result_type); + } + + vm_pop_frame(vm); + if (result_mem != NULL) { + return vm_load(vm, result_mem, result_type); + } + + vmValue void_result = {}; + return void_result; +} + + +ssaProcedure *vm_lookup_procedure(VirtualMachine *vm, String name) { + ssaValue *v = ssa_lookup_member(vm->module, name); + GB_ASSERT(v->kind == ssaValue_Proc); + return &v->Proc; +} + +vmValue vm_call_proc_by_name(VirtualMachine *vm, String name, Array args) { + return vm_call_proc(vm, vm_lookup_procedure(vm, name), args); +} + +vmValue vm_exact_value(VirtualMachine *vm, ssaValue *ptr, ExactValue value, Type *t) { + Type *original_type = t; + t = base_type(get_enum_base_type(t)); + // i64 size = vm_type_size_of(vm, t); + if (is_type_boolean(t)) { + return vm_make_value_int(original_type, value.value_bool); + } else if (is_type_integer(t)) { + return vm_make_value_int(original_type, value.value_integer); + } else if (is_type_float(t)) { + if (t->Basic.kind == Basic_f32) { + return vm_make_value_f32(original_type, cast(f32)value.value_float); + } else if (t->Basic.kind == Basic_f64) { + return vm_make_value_f64(original_type, cast(f64)value.value_float); + } + } else if (is_type_pointer(t)) { + return vm_make_value_ptr(original_type, cast(void *)cast(intptr)value.value_pointer); + } else if (is_type_string(t)) { + vmValue result = vm_make_value_comp(original_type, vm->stack_allocator, 2); + + String str = value.value_string; + i64 len = str.len; + u8 *text = gb_alloc_array(vm->heap_allocator, u8, len); + gb_memcopy(text, str.text, len); + + result.val_comp[0] = vm_make_value_ptr(t_u8_ptr, text); + result.val_comp[1] = vm_make_value_int(t_int, len); + + return result; + } else if (value.kind == ExactValue_Compound) { + if (ptr != NULL) { + vmValue *found = map_get(&vm->const_compound_lits, hash_pointer(ptr)); + if (found != NULL) { + return *found; + } + } + + ast_node(cl, CompoundLit, value.value_compound); + + if (is_type_array(t)) { + vmValue result = {}; + + isize elem_count = cl->elems.count; + if (elem_count == 0) { + if (ptr != NULL) { + map_set(&vm->const_compound_lits, hash_pointer(ptr), result); + } + return result; + } + + Type *type = base_type(t); + result = vm_make_value_comp(t, vm->heap_allocator, type->Array.count); + for (isize i = 0; i < elem_count; i++) { + TypeAndValue *tav = type_and_value_of_expression(vm->module->info, cl->elems[i]); + vmValue elem = vm_exact_value(vm, NULL, tav->value, tav->type); + result.val_comp[i] = elem; + } + + if (ptr != NULL) { + map_set(&vm->const_compound_lits, hash_pointer(ptr), result); + } + + return result; + } else if (is_type_vector(t)) { + vmValue result = {}; + + isize elem_count = cl->elems.count; + if (elem_count == 0) { + if (ptr != NULL) { + map_set(&vm->const_compound_lits, hash_pointer(ptr), result); + } + return result; + } + + Type *type = base_type(t); + result = vm_make_value_comp(t, vm->heap_allocator, type->Array.count); + for (isize i = 0; i < elem_count; i++) { + TypeAndValue *tav = type_and_value_of_expression(vm->module->info, cl->elems[i]); + vmValue elem = vm_exact_value(vm, NULL, tav->value, tav->type); + result.val_comp[i] = elem; + } + + if (ptr != NULL) { + map_set(&vm->const_compound_lits, hash_pointer(ptr), result); + } + + return result; + } else if (is_type_struct(t)) { + ast_node(cl, CompoundLit, value.value_compound); + + isize value_count = t->Record.field_count; + vmValue result = vm_make_value_comp(t, vm->heap_allocator, value_count); + + if (cl->elems.count == 0) { + return result; + } + + if (cl->elems[0]->kind == AstNode_FieldValue) { + isize elem_count = cl->elems.count; + for (isize i = 0; i < elem_count; i++) { + ast_node(fv, FieldValue, cl->elems[i]); + String name = fv->field->Ident.string; + + TypeAndValue *tav = type_and_value_of_expression(vm->module->info, fv->value); + GB_ASSERT(tav != NULL); + + Selection sel = lookup_field(vm->heap_allocator, t, name, false); + Entity *f = t->Record.fields[sel.index[0]]; + + result.val_comp[f->Variable.field_index] = vm_exact_value(vm, NULL, tav->value, f->type); + } + } else { + for (isize i = 0; i < value_count; i++) { + TypeAndValue *tav = type_and_value_of_expression(vm->module->info, cl->elems[i]); + GB_ASSERT(tav != NULL); + Entity *f = t->Record.fields_in_src_order[i]; + result.val_comp[f->Variable.field_index] = vm_exact_value(vm, NULL, tav->value, f->type); + } + } + + return result; + } else { + GB_PANIC("TODO(bill): Other compound types\n"); + } + + } else if (value.kind == ExactValue_Invalid) { + vmValue zero_result = {}; + zero_result.type = t; + return zero_result; + } else { + gb_printf_err("TODO(bill): Other constant types: %s\n", type_to_string(original_type)); + } + + GB_ASSERT_MSG(t == NULL, "%s - %d", type_to_string(t), value.kind); + vmValue void_result = {}; + return void_result; +} + + +vmValue vm_operand_value(VirtualMachine *vm, ssaValue *value) { + vmFrame *f = vm_back_frame(vm); + vmValue v = {}; + switch (value->kind) { + case ssaValue_Constant: { + v = vm_exact_value(vm, value, value->Constant.value, value->Constant.type); + } break; + case ssaValue_ConstantSlice: { + auto *cs = &value->ConstantSlice; + v = vm_make_value_comp(ssa_type(value), vm->stack_allocator, 3); + v.val_comp[0] = vm_operand_value(vm, cs->backing_array); + v.val_comp[1] = vm_make_value_int(t_int, cs->count); + v.val_comp[2] = vm_make_value_int(t_int, cs->count); + } break; + case ssaValue_Nil: + GB_PANIC("TODO(bill): ssaValue_Nil"); + break; + case ssaValue_TypeName: + GB_PANIC("ssaValue_TypeName has no operand value"); + 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.type = ssa_type(value); + v.val_proc.proc = &value->Proc; + // GB_PANIC("TODO(bill): ssaValue_Proc"); + } break; + case ssaValue_Block: + GB_PANIC("ssaValue_Block has no operand value"); + break; + case ssaValue_Instr: { + vmValue *found = map_get(&f->values, hash_pointer(value)); + if (found) { + v = *found; + } else { + GB_PANIC("Invalid instruction"); + } + } break; + } + + return v; +} + +void vm_store_integer(VirtualMachine *vm, void *dst, vmValue val) { + // TODO(bill): I assume little endian here + GB_ASSERT(dst != NULL); + Type *type = val.type; + GB_ASSERT_MSG(is_type_integer(type) || is_type_boolean(type), + "\nExpected integer/boolean, got %s (%s)", + type_to_string(type), + type_to_string(base_type(type))); + i64 size = vm_type_size_of(vm, type); + gb_memcopy(dst, &val.val_int, size); +} + +void vm_store_pointer(VirtualMachine *vm, void *dst, vmValue val) { + // TODO(bill): I assume little endian here + GB_ASSERT(dst != NULL); + GB_ASSERT(is_type_pointer(val.type)); + gb_memcopy(dst, &val.val_ptr, vm_type_size_of(vm, t_rawptr)); +} + + +void vm_store(VirtualMachine *vm, void *dst, vmValue val, Type *type) { + i64 size = vm_type_size_of(vm, type); + Type *original_type = type; + // NOTE(bill): enums are pretty much integers + type = base_type(get_enum_base_type(type)); + + 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); + break; + case Basic_f32: + *cast(f32 *)dst = val.val_f32; + break; + case Basic_f64: + *cast(f64 *)dst = val.val_f64; + break; + case Basic_rawptr: + vm_store_pointer(vm, dst, val); // NOTE(bill): A pointer can be treated as an integer + break; + case Basic_string: { + i64 word_size = vm_type_size_of(vm, t_int); + + u8 *mem = cast(u8 *)dst; + vm_store_pointer(vm, mem+0*word_size, val.val_comp[0]); + vm_store_integer(vm, mem+1*word_size, val.val_comp[1]); + } break; + case Basic_any: { + i64 word_size = vm_type_size_of(vm, t_int); + + u8 *mem = cast(u8 *)dst; + vm_store_pointer(vm, mem+0*word_size, val.val_comp[0]); + vm_store_pointer(vm, mem+1*word_size, val.val_comp[1]); + } break; + default: + gb_printf_err("TODO(bill): other basic types for `vm_store` %s\n", type_to_string(type)); + break; + } + break; + + case Type_Pointer: + vm_store_pointer(vm, dst, val); + break; + + case Type_Record: { + u8 *mem = cast(u8 *)dst; + gb_zero_size(mem, size); + + if (is_type_struct(type)) { + GB_ASSERT_MSG(type->Record.field_count >= val.val_comp.count, + "%td vs %td", + type->Record.field_count, val.val_comp.count); + + isize field_count = gb_min(val.val_comp.count, type->Record.field_count); + + for (isize i = 0; i < field_count; i++) { + Entity *f = type->Record.fields[i]; + i64 offset = vm_type_offset_of(vm, type, i); + vm_store(vm, mem+offset, val.val_comp[i], f->type); + } + } else if (is_type_union(type)) { + GB_ASSERT(val.val_comp.count == 2); + i64 word_size = vm_type_size_of(vm, t_int); + i64 size_of_union = vm_type_size_of(vm, type) - word_size; + for (isize i = 0; i < size_of_union; i++) { + mem[i] = cast(u8)val.val_comp[0].val_comp[i].val_int; + } + vm_store_integer(vm, mem + size_of_union, val.val_comp[1]); + + } else if (is_type_raw_union(type)) { + GB_ASSERT(val.val_comp.count == 1); + i64 word_size = vm_type_size_of(vm, t_int); + i64 size_of_union = vm_type_size_of(vm, type) - word_size; + for (isize i = 0; i < size_of_union; i++) { + mem[i] = cast(u8)val.val_comp[0].val_comp[i].val_int; + } + } else { + GB_PANIC("Unknown record type: %s", type_to_string(type)); + } + } break; + + case Type_Tuple: { + u8 *mem = cast(u8 *)dst; + + GB_ASSERT_MSG(type->Tuple.variable_count >= val.val_comp.count, + "%td vs %td", + type->Tuple.variable_count, val.val_comp.count); + + isize variable_count = gb_min(val.val_comp.count, type->Tuple.variable_count); + + for (isize i = 0; i < variable_count; i++) { + Entity *f = type->Tuple.variables[i]; + void *ptr = mem + vm_type_offset_of(vm, type, i); + vm_store(vm, ptr, val.val_comp[i], f->type); + } + } break; + + case Type_Array: { + Type *elem_type = type->Array.elem; + u8 *mem = cast(u8 *)dst; + i64 elem_size = vm_type_size_of(vm, elem_type); + i64 elem_count = gb_min(val.val_comp.count, type->Array.count); + + for (i64 i = 0; i < elem_count; i++) { + vm_store(vm, mem + i*elem_size, val.val_comp[i], elem_type); + } + } break; + + case Type_Vector: { + Type *elem_type = type->Array.elem; + GB_ASSERT_MSG(!is_type_boolean(elem_type), "TODO(bill): Booleans of vectors"); + u8 *mem = cast(u8 *)dst; + i64 elem_size = vm_type_size_of(vm, elem_type); + i64 elem_count = gb_min(val.val_comp.count, type->Array.count); + + for (i64 i = 0; i < elem_count; i++) { + vm_store(vm, mem + i*elem_size, val.val_comp[i], elem_type); + } + } break; + + case Type_Slice: { + i64 word_size = vm_type_size_of(vm, t_int); + + u8 *mem = cast(u8 *)dst; + vm_store_pointer(vm, mem+0*word_size, val.val_comp[0]); + vm_store_integer(vm, mem+1*word_size, val.val_comp[1]); + vm_store_integer(vm, mem+2*word_size, val.val_comp[2]); + } break; + + default: + gb_printf_err("TODO(bill): other types for `vm_store` %s\n", type_to_string(type)); + break; + } +} + +vmValue vm_load_integer(VirtualMachine *vm, void *ptr, Type *type) { + // TODO(bill): I assume little endian here + vmValue v = {}; + v.type = type; + GB_ASSERT(is_type_integer(type) || is_type_boolean(type)); + // NOTE(bill): Only load the needed amount + gb_memcopy(&v.val_int, ptr, vm_type_size_of(vm, type)); + return v; +} + +vmValue vm_load_pointer(VirtualMachine *vm, void *ptr, Type *type) { + // TODO(bill): I assume little endian here + vmValue v = {}; + v.type = type; + GB_ASSERT(is_type_pointer(type)); + // NOTE(bill): Only load the needed amount + gb_memcopy(&v.val_int, ptr, vm_type_size_of(vm, type)); + return v; +} + + +vmValue vm_load(VirtualMachine *vm, void *ptr, Type *type) { + i64 size = vm_type_size_of(vm, type); + Type *original_type = type; + type = base_type(get_enum_base_type(type)); + + 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: + return vm_load_integer(vm, ptr, original_type); + case Basic_f32: + return vm_make_value_f32(original_type, *cast(f32 *)ptr); + case Basic_f64: + return vm_make_value_f64(original_type, *cast(f64 *)ptr); + case Basic_rawptr: + return vm_load_pointer(vm, ptr, original_type); + + + case Basic_string: { + u8 *mem = cast(u8 *)ptr; + i64 word_size = vm_type_size_of(vm, t_int); + vmValue result = vm_make_value_comp(type, vm->stack_allocator, 2); + result.val_comp[0] = vm_load_pointer(vm, mem+0*word_size, t_u8_ptr); + result.val_comp[1] = vm_load_integer(vm, mem+1*word_size, t_int); + return result; + } break; + + default: + GB_PANIC("TODO(bill): other basic types for `vm_load` %s", type_to_string(type)); + break; + } + break; + + case Type_Pointer: + return vm_load_pointer(vm, ptr, original_type); + + case Type_Array: { + i64 count = type->Array.count; + Type *elem_type = type->Array.elem; + i64 elem_size = vm_type_size_of(vm, elem_type); + + vmValue result = vm_make_value_comp(type, vm->stack_allocator, count); + + u8 *mem = cast(u8 *)ptr; + for (isize i = 0; i < count; i++) { + i64 offset = elem_size*i; + vmValue val = vm_load(vm, mem+offset, elem_type); + result.val_comp[i] = val; + } + + return result; + } break; + + case Type_Slice: { + Type *elem_type = type->Slice.elem; + i64 elem_size = vm_type_size_of(vm, elem_type); + i64 word_size = vm_type_size_of(vm, t_int); + + vmValue result = vm_make_value_comp(type, vm->stack_allocator, 3); + + u8 *mem = cast(u8 *)ptr; + result.val_comp[0] = vm_load(vm, mem+0*word_size, t_rawptr); // data + result.val_comp[1] = vm_load(vm, mem+1*word_size, t_int); // count + result.val_comp[2] = vm_load(vm, mem+2*word_size, t_int); // capacity + return result; + } break; + + case Type_Record: { + if (is_type_struct(type)) { + isize field_count = type->Record.field_count; + + vmValue result = vm_make_value_comp(type, vm->stack_allocator, field_count); + + u8 *mem = cast(u8 *)ptr; + for (isize i = 0; i < field_count; i++) { + Entity *f = type->Record.fields[i]; + i64 offset = vm_type_offset_of(vm, type, i); + result.val_comp[i] = vm_load(vm, mem+offset, f->type); + } + + return result; + } else if (is_type_union(type)) { + i64 word_size = vm_type_size_of(vm, t_int); + i64 size_of_union = vm_type_size_of(vm, type) - word_size; + u8 *mem = cast(u8 *)ptr; + + vmValue result = vm_make_value_comp(type, vm->stack_allocator, 2); + result.val_comp[0] = vm_load(vm, mem, make_type_array(vm->stack_allocator, t_u8, size_of_union)); + result.val_comp[1] = vm_load(vm, mem+size_of_union, t_int); + return result; + } else if (is_type_raw_union(type)) { + gb_printf_err("TODO(bill): load raw_union\n"); + } else { + gb_printf_err("TODO(bill): load other records\n"); + } + } break; + + case Type_Tuple: { + isize count = type->Tuple.variable_count; + + vmValue result = vm_make_value_comp(type, vm->stack_allocator, count); + + u8 *mem = cast(u8 *)ptr; + for (isize i = 0; i < count; i++) { + Entity *f = type->Tuple.variables[i]; + i64 offset = vm_type_offset_of(vm, type, i); + result.val_comp[i] = vm_load(vm, mem+offset, f->type); + } + return result; + } break; + + default: + GB_PANIC("TODO(bill): other types for `vm_load` %s", type_to_string(type)); + break; + } + + GB_ASSERT(type == NULL); + vmValue void_result = {}; + return void_result; +} + +vmValue vm_exec_binary_op(VirtualMachine *vm, Type *type, vmValue lhs, vmValue rhs, TokenKind op) { + vmValue result = {}; + + type = base_type(type); + if (is_type_vector(type)) { + Type *elem = type->Vector.elem; + i64 count = type->Vector.count; + + result = vm_make_value_comp(type, vm->stack_allocator, count); + + for (i64 i = 0; i < count; i++) { + result.val_comp[i] = vm_exec_binary_op(vm, elem, lhs.val_comp[i], rhs.val_comp[i], op); + } + + return result; + } + + if (gb_is_between(op, Token__ComparisonBegin+1, Token__ComparisonEnd-1)) { + if (is_type_integer(type) || is_type_boolean(type)) { + // TODO(bill): Do I need to take into account the size of the integer? + switch (op) { + case Token_CmpEq: result.val_int = lhs.val_int == rhs.val_int; break; + case Token_NotEq: result.val_int = lhs.val_int != rhs.val_int; break; + case Token_Lt: result.val_int = lhs.val_int < rhs.val_int; break; + case Token_Gt: result.val_int = lhs.val_int > rhs.val_int; break; + case Token_LtEq: result.val_int = lhs.val_int <= rhs.val_int; break; + case Token_GtEq: result.val_int = lhs.val_int >= rhs.val_int; break; + } + } else if (type == t_f32) { + switch (op) { + case Token_CmpEq: result.val_f32 = lhs.val_f32 == rhs.val_f32; break; + case Token_NotEq: result.val_f32 = lhs.val_f32 != rhs.val_f32; break; + case Token_Lt: result.val_f32 = lhs.val_f32 < rhs.val_f32; break; + case Token_Gt: result.val_f32 = lhs.val_f32 > rhs.val_f32; break; + case Token_LtEq: result.val_f32 = lhs.val_f32 <= rhs.val_f32; break; + case Token_GtEq: result.val_f32 = lhs.val_f32 >= rhs.val_f32; break; + } + } else if (type == t_f64) { + switch (op) { + case Token_CmpEq: result.val_f64 = lhs.val_f64 == rhs.val_f64; break; + case Token_NotEq: result.val_f64 = lhs.val_f64 != rhs.val_f64; break; + case Token_Lt: result.val_f64 = lhs.val_f64 < rhs.val_f64; break; + case Token_Gt: result.val_f64 = lhs.val_f64 > rhs.val_f64; break; + case Token_LtEq: result.val_f64 = lhs.val_f64 <= rhs.val_f64; break; + case Token_GtEq: result.val_f64 = lhs.val_f64 >= rhs.val_f64; break; + } + } else if (is_type_string(type)) { + Array args = {}; + array_init_count(&args, vm->stack_allocator, 2); + args[0] = lhs; + args[1] = rhs; + switch (op) { + case Token_CmpEq: result = vm_call_proc_by_name(vm, make_string("__string_eq"), args); break; + case Token_NotEq: result = vm_call_proc_by_name(vm, make_string("__string_ne"), args); break; + case Token_Lt: result = vm_call_proc_by_name(vm, make_string("__string_lt"), args); break; + case Token_Gt: result = vm_call_proc_by_name(vm, make_string("__string_gt"), args); break; + case Token_LtEq: result = vm_call_proc_by_name(vm, make_string("__string_le"), args); break; + case Token_GtEq: result = vm_call_proc_by_name(vm, make_string("__string_ge"), args); break; + } + } else { + GB_PANIC("TODO(bill): Vector BinaryOp"); + } + } else { + if (is_type_integer(type) || is_type_boolean(type)) { + switch (op) { + case Token_Add: result.val_int = lhs.val_int + rhs.val_int; break; + case Token_Sub: result.val_int = lhs.val_int - rhs.val_int; break; + case Token_And: result.val_int = lhs.val_int & rhs.val_int; break; + case Token_Or: result.val_int = lhs.val_int | rhs.val_int; break; + case Token_Xor: result.val_int = lhs.val_int ^ rhs.val_int; break; + case Token_Shl: result.val_int = lhs.val_int << rhs.val_int; break; + case Token_Shr: result.val_int = lhs.val_int >> rhs.val_int; break; + case Token_Mul: result.val_int = lhs.val_int * rhs.val_int; break; + case Token_Not: result.val_int = lhs.val_int ^ rhs.val_int; break; + + case Token_AndNot: result.val_int = lhs.val_int & (~rhs.val_int); break; + + // TODO(bill): Take into account size of integer and signedness + 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(type)) { + if (type == t_f32) { + switch (op) { + case Token_Add: result.val_f32 = lhs.val_f32 + rhs.val_f32; break; + case Token_Sub: result.val_f32 = lhs.val_f32 - rhs.val_f32; break; + case Token_Mul: result.val_f32 = lhs.val_f32 * rhs.val_f32; break; + case Token_Quo: result.val_f32 = lhs.val_f32 / rhs.val_f32; break; + + case Token_Mod: GB_PANIC("TODO(bill): BinaryOp f32 Token_Mod"); break; + } + } else if (type == t_f64) { + switch (op) { + case Token_Add: result.val_f64 = lhs.val_f64 + rhs.val_f64; break; + case Token_Sub: result.val_f64 = lhs.val_f64 - rhs.val_f64; break; + case Token_Mul: result.val_f64 = lhs.val_f64 * rhs.val_f64; break; + case Token_Quo: result.val_f64 = lhs.val_f64 / rhs.val_f64; break; + + case Token_Mod: GB_PANIC("TODO(bill): BinaryOp f64 Token_Mod"); break; + } + } + } else { + GB_PANIC("Invalid binary op type"); + } + } + + return result; +} + +void vm_exec_instr(VirtualMachine *vm, ssaValue *value) { + GB_ASSERT(value != NULL); + 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 1 + Array args = {}; // Empty + vm_call_proc_by_name(vm, make_string(SSA_STARTUP_RUNTIME_PROC_NAME), args); // NOTE(bill): No return value +#endif + } break; + + case ssaInstr_Comment: + break; + + case ssaInstr_Local: { + Type *type = ssa_type(value); + GB_ASSERT(is_type_pointer(type)); + 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); + vm_set_value(f, value, vm_make_value_ptr(type, memory)); + array_add(&f->locals, memory); + } break; + + case ssaInstr_ZeroInit: { + Type *t = type_deref(ssa_type(instr->ZeroInit.address)); + vmValue addr = vm_operand_value(vm, instr->ZeroInit.address); + void *data = addr.val_ptr; + i64 size = vm_type_size_of(vm, t); + gb_zero_size(data, size); + } break; + + case ssaInstr_Store: { + vmValue addr = vm_operand_value(vm, instr->Store.address); + vmValue val = vm_operand_value(vm, instr->Store.value); + GB_ASSERT(val.type != NULL); + Type *t = type_deref(ssa_type(instr->Store.address)); + vm_store(vm, addr.val_ptr, val, t); + } break; + + case ssaInstr_Load: { + vmValue addr = vm_operand_value(vm, instr->Load.address); + Type *t = ssa_type(value); + vmValue v = vm_load(vm, addr.val_ptr, t); + 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); + GB_ASSERT(is_type_pointer(t)); + 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(t, 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); + GB_ASSERT(is_type_pointer(t)); + i64 offset = vm_type_offset_of(vm, type_deref(t), elem_index); + void *ptr = cast(u8 *)address.val_ptr + offset; + vm_set_value(f, value, vm_make_value_ptr(t, ptr)); + } break; + + case ssaInstr_PtrOffset: { + Type *t = ssa_type(instr->PtrOffset.address); + GB_ASSERT(is_type_pointer(t)); + 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(t, ptr)); + } break; + + case ssaInstr_Phi: { + for_array(i, f->curr_block->preds) { + ssaBlock *pred = f->curr_block->preds[i]; + if (f->prev_block == pred) { + vmValue edge = vm_operand_value(vm, instr->Phi.edges[i]); + vm_set_value(f, value, edge); + break; + } + } + } 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: { + vm_jump_block(f, instr->Jump.block); + } break; + + case ssaInstr_If: { + vmValue cond = vm_operand_value(vm, instr->If.cond); + if (cond.val_int != 0) { + vm_jump_block(f, instr->If.true_block); + } else { + vm_jump_block(f, instr->If.false_block); + } + } 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; + f->instr_index = 0; + return; + } break; + + case ssaInstr_Conv: { + // TODO(bill): Assuming little endian + vmValue dst = {}; + vmValue src = vm_operand_value(vm, instr->Conv.value); + i64 from_size = vm_type_size_of(vm, instr->Conv.from); + i64 to_size = vm_type_size_of(vm, instr->Conv.to); + switch (instr->Conv.kind) { + case ssaConv_trunc: + gb_memcopy(&dst, &src, to_size); + break; + case ssaConv_zext: + gb_memcopy(&dst, &src, from_size); + break; + case ssaConv_fptrunc: { + GB_ASSERT(from_size > to_size); + GB_ASSERT(base_type(instr->Conv.from) == t_f64); + GB_ASSERT(base_type(instr->Conv.to) == t_f32); + dst.val_f32 = cast(f32)src.val_f64; + } break; + case ssaConv_fpext: { + GB_ASSERT(from_size < to_size); + GB_ASSERT(base_type(instr->Conv.from) == t_f32); + GB_ASSERT(base_type(instr->Conv.to) == t_f64); + dst.val_f64 = cast(f64)src.val_f32; + } break; + case ssaConv_fptoui: { + Type *from = base_type(instr->Conv.from); + if (from == t_f64) { + u64 u = cast(u64)src.val_f64; + vm_store_integer(vm, &dst, vm_make_value_int(instr->Conv.to, u)); + } else { + u64 u = cast(u64)src.val_f32; + vm_store_integer(vm, &dst, vm_make_value_int(instr->Conv.to, u)); + } + } break; + case ssaConv_fptosi: { + Type *from = base_type(instr->Conv.from); + if (from == t_f64) { + i64 i = cast(i64)src.val_f64; + vm_store_integer(vm, &dst, vm_make_value_int(instr->Conv.to, i)); + } else { + i64 i = cast(i64)src.val_f32; + vm_store_integer(vm, &dst, vm_make_value_int(instr->Conv.to, i)); + } + } break; + case ssaConv_uitofp: { + Type *to = base_type(instr->Conv.to); + if (to == t_f64) { + dst = vm_make_value_f64(instr->Conv.to, cast(f64)cast(u64)src.val_int); + } else { + dst = vm_make_value_f32(instr->Conv.to, cast(f32)cast(u64)src.val_int); + } + } break; + case ssaConv_sitofp: { + Type *to = base_type(instr->Conv.to); + if (to == t_f64) { + dst = vm_make_value_f64(instr->Conv.to, cast(f64)cast(i64)src.val_int); + } else { + dst = vm_make_value_f32(instr->Conv.to, cast(f32)cast(i64)src.val_int); + } + } break; + + case ssaConv_ptrtoint: + dst = vm_make_value_int(instr->Conv.to, cast(i64)src.val_ptr); + break; + case ssaConv_inttoptr: + dst = vm_make_value_ptr(instr->Conv.to, cast(void *)src.val_int); + break; + case ssaConv_bitcast: + dst = src; + dst.type = instr->Conv.to; + break; + } + + vm_set_value(f, value, dst); + } break; + + case ssaInstr_Unreachable: { + GB_PANIC("Unreachable"); + } break; + + case ssaInstr_BinaryOp: { + auto *bo = &instr->BinaryOp; + Type *type = ssa_type(bo->left); + vmValue lhs = vm_operand_value(vm, bo->left); + vmValue rhs = vm_operand_value(vm, bo->right); + vmValue v = vm_exec_binary_op(vm, type, lhs, rhs, bo->op); + 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_proc(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: { + vmValue vector = vm_operand_value(vm, instr->VectorExtractElement.vector); + vmValue index = vm_operand_value(vm, instr->VectorExtractElement.index); + vmValue v = vector.val_comp[index.val_int]; + vm_set_value(f, value, v); + } break; + + case ssaInstr_VectorInsertElement: { + vmValue vector = vm_operand_value(vm, instr->VectorInsertElement.vector); + vmValue elem = vm_operand_value(vm, instr->VectorInsertElement.elem); + vmValue index = vm_operand_value(vm, instr->VectorInsertElement.index); + vector.val_comp[index.val_int] = elem; + } break; + + case ssaInstr_VectorShuffle: { + auto *vs = &instr->VectorShuffle; + vmValue old_vector = vm_operand_value(vm, instr->VectorShuffle.vector); + vmValue new_vector = vm_make_value_comp(ssa_type(value), vm->stack_allocator, vs->index_count); + + for (i32 i = 0; i < vs->index_count; i++) { + new_vector.val_comp[i] = old_vector.val_comp[vs->indices[i]]; + } + + vm_set_value(f, value, new_vector); + } break; + + case ssaInstr_BoundsCheck: { + auto *bc = &instr->BoundsCheck; + Array args = {}; + array_init(&args, vm->stack_allocator, 5); + array_add(&args, vm_exact_value(vm, NULL, make_exact_value_string(bc->pos.file), t_string)); + array_add(&args, vm_exact_value(vm, NULL, make_exact_value_integer(bc->pos.line), t_int)); + array_add(&args, vm_exact_value(vm, NULL, make_exact_value_integer(bc->pos.column), t_int)); + array_add(&args, vm_operand_value(vm, bc->index)); + array_add(&args, vm_operand_value(vm, bc->len)); + + vm_call_proc_by_name(vm, make_string("__bounds_check_error"), args); + } break; + + case ssaInstr_SliceBoundsCheck: { + auto *bc = &instr->SliceBoundsCheck; + Array args = {}; + + array_init(&args, vm->stack_allocator, 7); + array_add(&args, vm_exact_value(vm, NULL, make_exact_value_string(bc->pos.file), t_string)); + array_add(&args, vm_exact_value(vm, NULL, make_exact_value_integer(bc->pos.line), t_int)); + array_add(&args, vm_exact_value(vm, NULL, make_exact_value_integer(bc->pos.column), t_int)); + array_add(&args, vm_operand_value(vm, bc->low)); + array_add(&args, vm_operand_value(vm, bc->high)); + if (!bc->is_substring) { + array_add(&args, vm_operand_value(vm, bc->max)); + vm_call_proc_by_name(vm, make_string("__slice_expr_error"), args); + } else { + vm_call_proc_by_name(vm, make_string("__substring_expr_error"), args); + } + } break; + + default: { + GB_PANIC(" %d\n", instr->kind); + } break; + } +} + + + +void vm_print_value(vmValue value, Type *type) { + type = base_type(type); + if (is_type_string(type)) { + vmValue data = value.val_comp[0]; + vmValue count = value.val_comp[1]; + gb_printf("`%.*s`", cast(int)count.val_int, cast(u8 *)data.val_ptr); + } else if (is_type_boolean(type)) { + if (value.val_int != 0) { + gb_printf("true"); + } else { + gb_printf("false"); + } + } else if (is_type_integer(type)) { + gb_printf("%lld", cast(i64)value.val_int); + } else if (type == t_f32) { + gb_printf("%f", value.val_f32); + } else if (type == t_f64) { + gb_printf("%f", value.val_f64); + } else if (is_type_pointer(type)) { + gb_printf("0x%08x", value.val_ptr); + } else if (is_type_array(type)) { + gb_printf("["); + for_array(i, value.val_comp) { + if (i > 0) { + gb_printf(", "); + } + vm_print_value(value.val_comp[i], type->Array.elem); + } + gb_printf("]"); + } else if (is_type_vector(type)) { + gb_printf("<"); + for_array(i, value.val_comp) { + if (i > 0) { + gb_printf(", "); + } + vm_print_value(value.val_comp[i], type->Vector.elem); + } + gb_printf(">"); + } else if (is_type_slice(type)) { + gb_printf("["); + for_array(i, value.val_comp) { + if (i > 0) { + gb_printf(", "); + } + vm_print_value(value.val_comp[i], type->Slice.elem); + } + gb_printf("]"); + } else if (is_type_maybe(type)) { + if (value.val_comp[1].val_int != 0) { + gb_printf("?"); + vm_print_value(value.val_comp[0], type->Maybe.elem); + } else { + gb_printf("nil"); + } + } else if (is_type_struct(type)) { + if (value.val_comp.count == 0) { + gb_printf("nil"); + } else { + gb_printf("{"); + for_array(i, value.val_comp) { + if (i > 0) { + gb_printf(", "); + } + vm_print_value(value.val_comp[i], type->Record.fields[i]->type); + } + gb_printf("}"); + } + } else if (is_type_tuple(type)) { + if (value.val_comp.count != 1) { + gb_printf("("); + } + for_array(i, value.val_comp) { + if (i > 0) { + gb_printf(", "); + } + vm_print_value(value.val_comp[i], type->Tuple.variables[i]->type); + } + if (value.val_comp.count != 1) { + gb_printf(")"); + } + } +} diff --git a/src/ssa.cpp b/src/ssa.cpp index 884eef685..58028850e 100644 --- a/src/ssa.cpp +++ b/src/ssa.cpp @@ -1146,9 +1146,11 @@ ssaDefer ssa_add_defer_instr(ssaProcedure *proc, isize scope_index, ssaValue *in ssaValue *ssa_add_module_constant(ssaModule *m, Type *type, ExactValue value) { + gbAllocator a = m->allocator; + // gbAllocator a = gb_heap_allocator(); + if (is_type_slice(type)) { ast_node(cl, CompoundLit, value.value_compound); - gbAllocator a = m->allocator; isize count = cl->elems.count; if (count == 0) { @@ -1174,11 +1176,14 @@ ssaValue *ssa_add_module_constant(ssaModule *m, Type *type, ExactValue value) { return ssa_make_value_constant_slice(a, type, g, count); } - return ssa_make_value_constant(m->allocator, type, value); + return ssa_make_value_constant(a, type, value); } ssaValue *ssa_add_global_string_array(ssaModule *m, String string) { + // TODO(bill): Should this use the arena allocator or the heap allocator? + // Strings could be huge! gbAllocator a = m->allocator; + // gbAllocator a = gb_heap_allocator(); isize max_len = 6+8+1; u8 *str = cast(u8 *)gb_alloc_array(a, u8, max_len); @@ -5334,6 +5339,13 @@ void ssa_gen_tree(ssaGen *s) { ssa_build_proc(m->procs[i], m->procs[i]->Proc.parent); } + // { + // DWORD old_protect = 0; + // DWORD new_protect = PAGE_READONLY; + // BOOL ok = VirtualProtect(m->arena.physical_start, m->arena.total_size, new_protect, &old_protect); + // } + + // m->layout = make_string("e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64"); } diff --git a/src/ssa_to_llvm.cpp b/src/ssa_to_llvm.cpp index bc67136c8..9d0e69eda 100644 --- a/src/ssa_to_llvm.cpp +++ b/src/ssa_to_llvm.cpp @@ -175,7 +175,7 @@ void ssa_print_type(ssaFileBuffer *f, ssaModule *m, Type *t) { case Type_Named: if (is_type_struct(t) || is_type_union(t)) { String *name = map_get(&m->type_names, hash_pointer(t)); - GB_ASSERT(name != NULL); + GB_ASSERT_MSG(name != NULL, "%.*s", LIT(t->Named.name)); ssa_print_encoded_local(f, *name); // ssa_print_encoded_local(f, t->Named.name); } else { diff --git a/src/vm.cpp b/src/vm.cpp index f037cadaa..a3f1f7f98 100644 --- a/src/vm.cpp +++ b/src/vm.cpp @@ -1,1182 +1,2 @@ +// TODO(bill): COMPLETELY REWORK THIS ENTIRE INTERPRETER #include "dyncall/include/dyncall.h" - -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; -} - -vmValue vm_make_value_int(i64 i) { - vmValue v = {}; - v.val_int = i; - return v; -} - - - -struct vmFrame { - VirtualMachine * vm; - vmFrame * caller; - ssaProcedure * curr_proc; - ssaBlock * prev_block; - ssaBlock * curr_block; - i32 instr_index; // For the current block - - 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 * - Map const_compound_lits; // Key: ssaValue * - vmValue exit_value; -}; - -void vm_exec_instr (VirtualMachine *vm, ssaValue *value); -vmValue vm_operand_value(VirtualMachine *vm, ssaValue *value); -void vm_store (VirtualMachine *vm, void *dst, vmValue val, Type *type); -void vm_print_value (vmValue value, Type *type); - -void vm_jump_block(vmFrame *f, ssaBlock *target) { - f->prev_block = f->curr_block; - f->curr_block = target; - f->instr_index = 0; -} - - -vmFrame *vm_back_frame(VirtualMachine *vm) { - if (vm->frame_stack.count > 0) { - return &vm->frame_stack[vm->frame_stack.count-1]; - } - return NULL; -} - -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 index) { - return type_offset_of(vm->module->sizes, vm->heap_allocator, type, index); -} - - -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); - map_init(&vm->const_compound_lits, vm->heap_allocator); - - for_array(i, vm->module->values.entries) { - ssaValue *v = vm->module->values.entries[i].value; - switch (v->kind) { - case ssaValue_Global: { - Type *t = ssa_type(v); - i64 size = vm_type_size_of(vm, t); - i64 align = vm_type_align_of(vm, t); - void *mem = gb_alloc_align(vm->heap_allocator, size, align); - vmValue init = vm_make_value_ptr(mem); - if (v->Global.value != NULL && v->Global.value->kind == ssaValue_Constant) { - vmValue *address = cast(vmValue *)init.val_ptr; - vm_store(vm, address, vm_operand_value(vm, v->Global.value), type_deref(t)); - } - map_set(&vm->globals, hash_pointer(v), init); - } break; - } - } - -} -void vm_destroy(VirtualMachine *vm) { - array_free(&vm->frame_stack); - map_destroy(&vm->globals); - map_destroy(&vm->const_compound_lits); - gb_arena_free(&vm->stack_arena); -} - - - - - - -void vm_set_value(vmFrame *f, ssaValue *v, vmValue val) { - if (v != NULL) { - GB_ASSERT(ssa_type(v) != NULL); - 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.prev_block = proc->blocks[0]; - 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_proc(VirtualMachine *vm, ssaProcedure *proc, Array values) { - Type *type = base_type(proc->type); - GB_ASSERT_MSG(type->Proc.param_count == values.count, - "Incorrect number of arguments passed into procedure call!\n" - "%.*s -> %td vs %td", - LIT(proc->name), - type->Proc.param_count, values.count); - - - vmValue result = {}; - - if (proc->body == NULL) { - // GB_PANIC("TODO(bill): external procedure"); - gb_printf_err("TODO(bill): external procedure: %.*s\n", LIT(proc->name)); - 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]); - } - - if (proc->name == SSA_STARTUP_RUNTIME_PROC_NAME) { - ssaBlock *block = proc->curr_block; - - } - - while (f->curr_block != NULL) { - ssaValue *curr_instr = f->curr_block->instrs[f->instr_index++]; - vm_exec_instr(vm, curr_instr); - } - - Type *proc_type = base_type(proc->type); - if (proc_type->Proc.result_count > 0) { - result = f->result; - - Type *rt = base_type(proc_type->Proc.results); - GB_ASSERT(is_type_tuple(rt)); - - if (rt->Tuple.variable_count == 1) { - rt = base_type(rt->Tuple.variables[0]->type); - } - - gb_printf("%.*s -> ", LIT(proc->name)); - vm_print_value(result, rt); - gb_printf("\n"); - } - - vm_pop_frame(vm); - return result; -} - - -ssaProcedure *vm_lookup_procedure(VirtualMachine *vm, String name) { - ssaValue *v = ssa_lookup_member(vm->module, name); - GB_ASSERT(v->kind == ssaValue_Proc); - ssaProcedure *proc = &v->Proc; - return proc; -} - -vmValue vm_call_proc_by_name(VirtualMachine *vm, String name, Array args) { - ssaProcedure *proc = vm_lookup_procedure(vm, name); - return vm_call_proc(vm, proc, args); -} - -vmValue vm_exact_value(VirtualMachine *vm, ssaValue *ptr, ExactValue value, Type *t) { - vmValue result = {}; - Type *original_type = t; - t = base_type(get_enum_base_type(t)); - // i64 size = vm_type_size_of(vm, t); - if (is_type_boolean(t)) { - result.val_int = value.value_bool != 0; - } else if (is_type_integer(t)) { - result.val_int = value.value_integer; - } else if (is_type_float(t)) { - if (t->Basic.kind == Basic_f32) { - result.val_f32 = cast(f32)value.value_float; - } else if (t->Basic.kind == Basic_f64) { - result.val_f64 = cast(f64)value.value_float; - } - } else if (is_type_pointer(t)) { - result.val_ptr = cast(void *)cast(intptr)value.value_pointer; - } else if (is_type_string(t)) { - array_init(&result.val_comp, vm->heap_allocator, 2); - - String str = 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(&result.val_comp, data); - array_add(&result.val_comp, count); - } else if (value.kind == ExactValue_Compound) { - if (ptr != NULL) { - vmValue *found = map_get(&vm->const_compound_lits, hash_pointer(ptr)); - if (found != NULL) { - return *found; - } - } - - ast_node(cl, CompoundLit, value.value_compound); - - if (is_type_array(t)) { - vmValue result = {}; - - isize elem_count = cl->elems.count; - if (elem_count == 0) { - if (ptr != NULL) { - map_set(&vm->const_compound_lits, hash_pointer(ptr), result); - } - return result; - } - - Type *type = base_type(t); - array_init_count(&result.val_comp, vm->heap_allocator, type->Array.count); - for (isize i = 0; i < elem_count; i++) { - TypeAndValue *tav = type_and_value_of_expression(vm->module->info, cl->elems[i]); - vmValue elem = vm_exact_value(vm, NULL, tav->value, tav->type); - result.val_comp[i] = elem; - } - - if (ptr != NULL) { - map_set(&vm->const_compound_lits, hash_pointer(ptr), result); - } - - return result; - } else if (is_type_struct(t)) { - ast_node(cl, CompoundLit, value.value_compound); - - if (cl->elems.count == 0) { - return result; - } - - isize value_count = t->Record.field_count; - array_init_count(&result.val_comp, vm->heap_allocator, value_count); - - if (cl->elems[0]->kind == AstNode_FieldValue) { - isize elem_count = cl->elems.count; - for (isize i = 0; i < elem_count; i++) { - ast_node(fv, FieldValue, cl->elems[i]); - String name = fv->field->Ident.string; - - TypeAndValue *tav = type_and_value_of_expression(vm->module->info, fv->value); - GB_ASSERT(tav != NULL); - - Selection sel = lookup_field(vm->heap_allocator, t, name, false); - Entity *f = t->Record.fields[sel.index[0]]; - - result.val_comp[f->Variable.field_index] = vm_exact_value(vm, NULL, tav->value, f->type); - } - } else { - for (isize i = 0; i < value_count; i++) { - TypeAndValue *tav = type_and_value_of_expression(vm->module->info, cl->elems[i]); - GB_ASSERT(tav != NULL); - Entity *f = t->Record.fields_in_src_order[i]; - result.val_comp[f->Variable.field_index] = vm_exact_value(vm, NULL, tav->value, f->type); - } - } - } else { - GB_PANIC("TODO(bill): Other compound types\n"); - } - - } else if (value.kind == ExactValue_Invalid) { - // NOTE(bill): "zero value" - } else { - gb_printf_err("TODO(bill): Other constant types: %s\n", type_to_string(original_type)); - } - - return result; -} - - -vmValue vm_operand_value(VirtualMachine *vm, ssaValue *value) { - vmFrame *f = vm_back_frame(vm); - vmValue v = {}; - switch (value->kind) { - case ssaValue_Constant: { - v = vm_exact_value(vm, value, value->Constant.value, value->Constant.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, void *dst, vmValue val, i64 store_bytes) { - // TODO(bill): I assume little endian here - GB_ASSERT(dst != NULL); - gb_memcopy(dst, &val.val_int, store_bytes); -} - -void vm_store(VirtualMachine *vm, void *dst, vmValue val, Type *type) { - i64 size = vm_type_size_of(vm, type); - Type *original_type = type; - type = base_type(get_enum_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: - *cast(f32 *)dst = val.val_f32; - break; - case Basic_f64: - *cast(f64 *)dst = val.val_f64; - break; - case Basic_rawptr: - *cast(void **)dst = val.val_ptr; - break; - case Basic_string: { - u8 *data = cast(u8 *)val.val_comp[0].val_ptr; - i64 word_size = vm_type_size_of(vm, t_int); - - u8 *mem = cast(u8 *)dst; - gb_memcopy(mem, data, word_size); - vm_store_integer(vm, mem+word_size, val.val_comp[1], word_size); - } break; - case Basic_any: { - void *type_info = val.val_comp[0].val_ptr; - void *data = val.val_comp[1].val_ptr; - i64 word_size = vm_type_size_of(vm, t_int); - - u8 *mem = cast(u8 *)dst; - gb_memcopy(mem, type_info, word_size); - gb_memcopy(mem+word_size, data, word_size); - } break; - default: - gb_printf_err("TODO(bill): other basic types for `vm_store` %s\n", type_to_string(type)); - break; - } - break; - - case Type_Pointer: - *cast(void **)dst = val.val_ptr; - break; - - case Type_Record: { - if (is_type_struct(type)) { - u8 *mem = cast(u8 *)dst; - - GB_ASSERT_MSG(type->Record.field_count >= val.val_comp.count, - "%td vs %td", - type->Record.field_count, val.val_comp.count); - - isize field_count = gb_min(val.val_comp.count, type->Record.field_count); - - for (isize i = 0; i < field_count; i++) { - Entity *f = type->Record.fields[i]; - i64 offset = vm_type_offset_of(vm, type, i); - vm_store(vm, mem+offset, val.val_comp[i], f->type); - } - } else { - // u8 *mem = cast(u8 *)dst; - // if (val.val_comp.count == 0) { - // gb_printf_err("%s\n", type_to_string(original_type)); - // // gb_zero_size(mem, vm_type_size_of(vm, type)); - // } else { - // GB_ASSERT(val.val_comp.count == 2); - // i64 word_size = vm_type_size_of(vm, t_int); - // i64 size_of_union = vm_type_size_of(vm, type) - word_size; - // for (isize i = 0; i < size_of_union; i++) { - // mem[i] = cast(u8)val.val_comp[0].val_comp[i].val_int; - // } - // vm_store_integer(vm, mem + size_of_union, val.val_comp[0], word_size); - // } - - // gb_printf_err("TODO(bill): records for `vm_store` %s\n", type_to_string(original_type)); - } - } break; - - case Type_Tuple: { - u8 *mem = cast(u8 *)dst; - - GB_ASSERT_MSG(type->Tuple.variable_count >= val.val_comp.count, - "%td vs %td", - type->Tuple.variable_count, val.val_comp.count); - - isize variable_count = gb_min(val.val_comp.count, type->Tuple.variable_count); - - for (isize i = 0; i < variable_count; i++) { - Entity *f = type->Tuple.variables[i]; - void *ptr = mem + vm_type_offset_of(vm, type, i); - vmValue member = val.val_comp[i]; - vm_store(vm, ptr, member, f->type); - } - } break; - - case Type_Array: { - Type *elem_type = type->Array.elem; - u8 *mem = cast(u8 *)dst; - i64 elem_size = vm_type_size_of(vm, elem_type); - i64 elem_count = gb_min(val.val_comp.count, type->Array.count); - - for (i64 i = 0; i < elem_count; i++) { - void *ptr = mem + (elem_size*i); - vmValue member = val.val_comp[i]; - vm_store(vm, ptr, member, elem_type); - } - } break; - - case Type_Slice: { - i64 word_size = vm_type_size_of(vm, t_int); - - u8 *mem = cast(u8 *)dst; - vm_store(vm, mem+0*word_size, val.val_comp[0], t_rawptr); - vm_store(vm, mem+1*word_size, val.val_comp[1], t_int); - vm_store(vm, mem+2*word_size, val.val_comp[2], t_int); - } break; - - default: - gb_printf_err("TODO(bill): other types for `vm_store` %s\n", type_to_string(type)); - break; - } -} - -vmValue vm_load_integer(VirtualMachine *vm, void *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, store_bytes); - return v; -} - -vmValue vm_load(VirtualMachine *vm, void *ptr, Type *type) { - i64 size = vm_type_size_of(vm, type); - type = base_type(get_enum_base_type(type)); - - vmValue result = {}; - - 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: - result = vm_load_integer(vm, ptr, size); - break; - case Basic_f32: - result.val_f32 = *cast(f32 *)ptr; - break; - case Basic_f64: - result.val_f64 = *cast(f64 *)ptr; - break; - case Basic_rawptr: - result.val_ptr = *cast(void **)ptr; - break; - - case Basic_string: { - i64 word_size = vm_type_size_of(vm, t_int); - u8 *mem = cast(u8 *)ptr; - array_init_count(&result.val_comp, vm->heap_allocator, 2); - - i64 count = 0; - u8 *data = mem + 0*word_size; - u8 *count_data = mem + 1*word_size; - switch (word_size) { - case 4: count = *cast(i32 *)count_data; break; - case 8: count = *cast(i64 *)count_data; break; - default: GB_PANIC("Unknown int size"); break; - } - - result.val_comp[0].val_ptr = mem; - result.val_comp[1].val_int = count; - - } break; - - default: - GB_PANIC("TODO(bill): other basic types for `vm_load` %s", type_to_string(type)); - break; - } - break; - - case Type_Pointer: - result.val_ptr = *cast(void **)ptr; - break; - - case Type_Array: { - i64 count = type->Array.count; - Type *elem_type = type->Array.elem; - i64 elem_size = vm_type_size_of(vm, elem_type); - - array_init_count(&result.val_comp, vm->heap_allocator, count); - - u8 *mem = cast(u8 *)ptr; - for (isize i = 0; i < count; i++) { - i64 offset = elem_size*i; - vmValue val = vm_load(vm, mem+offset, elem_type); - result.val_comp[i] = val; - } - } break; - - case Type_Slice: { - Type *elem_type = type->Slice.elem; - i64 elem_size = vm_type_size_of(vm, elem_type); - i64 word_size = vm_type_size_of(vm, t_int); - - array_init_count(&result.val_comp, vm->heap_allocator, 3); - - u8 *mem = cast(u8 *)ptr; - result.val_comp[0] = vm_load(vm, mem+0*word_size, t_rawptr); // data - result.val_comp[1] = vm_load(vm, mem+1*word_size, t_int); // count - result.val_comp[2] = vm_load(vm, mem+2*word_size, t_int); // capacity - return result; - } break; - - case Type_Record: { - if (is_type_struct(type)) { - isize field_count = type->Record.field_count; - - array_init_count(&result.val_comp, vm->heap_allocator, field_count); - - u8 *mem = cast(u8 *)ptr; - for (isize i = 0; i < field_count; i++) { - Entity *f = type->Record.fields[i]; - i64 offset = vm_type_offset_of(vm, type, i); - vmValue val = vm_load(vm, mem+offset, f->type); - result.val_comp[i] = val; - } - } - } break; - - case Type_Tuple: { - isize count = type->Tuple.variable_count; - - array_init_count(&result.val_comp, vm->heap_allocator, count); - - u8 *mem = cast(u8 *)ptr; - for (isize i = 0; i < count; i++) { - Entity *f = type->Tuple.variables[i]; - i64 offset = vm_type_offset_of(vm, type, i); - vmValue val = vm_load(vm, mem+offset, f->type); - result.val_comp[i] = val; - } - } break; - - default: - GB_PANIC("TODO(bill): other types for `vm_load` %s", type_to_string(type)); - break; - } - - return result; -} - -void vm_exec_instr(VirtualMachine *vm, ssaValue *value) { - GB_ASSERT(value != NULL); - 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 1 - ssaProcedure *proc = vm_lookup_procedure(vm, make_string(SSA_STARTUP_RUNTIME_PROC_NAME)); - Array args = {}; // Empty - vm_call_proc(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: { - Type *t = type_deref(ssa_type(instr->ZeroInit.address)); - vmValue addr = vm_operand_value(vm, instr->ZeroInit.address); - void *data = addr.val_ptr; - i64 size = vm_type_size_of(vm, t); - gb_zero_size(data, size); - } break; - - case ssaInstr_Store: { - vmValue addr = vm_operand_value(vm, instr->Store.address); - vmValue val = vm_operand_value(vm, instr->Store.value); - Type *t = ssa_type(instr->Store.value); - vm_store(vm, addr.val_ptr, val, t); - } break; - - case ssaInstr_Load: { - vmValue addr = vm_operand_value(vm, instr->Load.address); - vmValue v = vm_load(vm, addr.val_ptr, 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 = vm_type_offset_of(vm, type_deref(t), elem_index); - void *ptr = cast(u8 *)address.val_ptr + offset; - 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: { - for_array(i, f->curr_block->preds) { - ssaBlock *pred = f->curr_block->preds[i]; - if (f->prev_block == pred) { - vmValue edge = vm_operand_value(vm, instr->Phi.edges[i]); - vm_set_value(f, value, edge); - break; - } - } - } 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: { - vm_jump_block(f, instr->Jump.block); - } break; - - case ssaInstr_If: { - vmValue cond = vm_operand_value(vm, instr->If.cond); - if (cond.val_int != 0) { - vm_jump_block(f, instr->If.true_block); - } else { - vm_jump_block(f, instr->If.false_block); - } - } 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; - f->instr_index = 0; - return; - } break; - - case ssaInstr_Conv: { - // TODO(bill): Assuming little endian - vmValue dst = {}; - vmValue src = vm_operand_value(vm, instr->Conv.value); - i64 from_size = vm_type_size_of(vm, instr->Conv.from); - i64 to_size = vm_type_size_of(vm, instr->Conv.to); - switch (instr->Conv.kind) { - case ssaConv_trunc: - gb_memcopy(&dst, &src, to_size); - break; - case ssaConv_zext: - gb_memcopy(&dst, &src, from_size); - break; - case ssaConv_fptrunc: { - GB_ASSERT(from_size > to_size); - GB_ASSERT(base_type(instr->Conv.from) == t_f64); - GB_ASSERT(base_type(instr->Conv.to) == t_f32); - dst.val_f32 = cast(f32)src.val_f64; - } break; - case ssaConv_fpext: { - GB_ASSERT(from_size < to_size); - GB_ASSERT(base_type(instr->Conv.from) == t_f32); - GB_ASSERT(base_type(instr->Conv.to) == t_f64); - dst.val_f64 = cast(f64)src.val_f32; - } break; - case ssaConv_fptoui: { - Type *from = base_type(instr->Conv.from); - if (from == t_f64) { - u64 u = cast(u64)src.val_f64; - vm_store_integer(vm, &dst, vm_make_value_int(u), to_size); - } else { - u64 u = cast(u64)src.val_f32; - vm_store_integer(vm, &dst, vm_make_value_int(u), to_size); - } - } break; - case ssaConv_fptosi: { - Type *from = base_type(instr->Conv.from); - if (from == t_f64) { - i64 i = cast(i64)src.val_f64; - vm_store_integer(vm, &dst, vm_make_value_int(i), to_size); - } else { - i64 i = cast(i64)src.val_f32; - vm_store_integer(vm, &dst, vm_make_value_int(i), to_size); - } - } break; - case ssaConv_uitofp: { - Type *to = base_type(instr->Conv.to); - if (to == t_f64) { - dst.val_f64 = cast(f64)cast(u64)src.val_int; - } else { - dst.val_f32 = cast(f32)cast(u64)src.val_int; - } - } break; - case ssaConv_sitofp: { - Type *to = base_type(instr->Conv.to); - if (to == t_f64) { - dst.val_f64 = cast(f64)cast(i64)src.val_int; - } else { - dst.val_f32 = cast(f32)cast(i64)src.val_int; - } - } break; - - case ssaConv_ptrtoint: - dst.val_int = cast(i64)src.val_ptr; - break; - case ssaConv_inttoptr: - dst.val_ptr = cast(void *)src.val_int; - break; - case ssaConv_bitcast: - dst = src; - break; - } - - vm_set_value(f, value, dst); - } 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)) { - vmValue v = {}; - vmValue l = vm_operand_value(vm, bo->left); - vmValue r = vm_operand_value(vm, bo->right); - - if (is_type_integer(t)) { - // TODO(bill): Do I need to take into account the size of the integer? - switch (bo->op) { - case Token_CmpEq: v.val_int = l.val_int == r.val_int; break; - case Token_NotEq: v.val_int = l.val_int != r.val_int; break; - case Token_Lt: v.val_int = l.val_int < r.val_int; break; - case Token_Gt: v.val_int = l.val_int > r.val_int; break; - case Token_LtEq: v.val_int = l.val_int <= r.val_int; break; - case Token_GtEq: v.val_int = l.val_int >= r.val_int; break; - } - } else if (t == t_f32) { - switch (bo->op) { - case Token_CmpEq: v.val_f32 = l.val_f32 == r.val_f32; break; - case Token_NotEq: v.val_f32 = l.val_f32 != r.val_f32; break; - case Token_Lt: v.val_f32 = l.val_f32 < r.val_f32; break; - case Token_Gt: v.val_f32 = l.val_f32 > r.val_f32; break; - case Token_LtEq: v.val_f32 = l.val_f32 <= r.val_f32; break; - case Token_GtEq: v.val_f32 = l.val_f32 >= r.val_f32; break; - } - } else if (t == t_f64) { - switch (bo->op) { - case Token_CmpEq: v.val_f64 = l.val_f64 == r.val_f64; break; - case Token_NotEq: v.val_f64 = l.val_f64 != r.val_f64; break; - case Token_Lt: v.val_f64 = l.val_f64 < r.val_f64; break; - case Token_Gt: v.val_f64 = l.val_f64 > r.val_f64; break; - case Token_LtEq: v.val_f64 = l.val_f64 <= r.val_f64; break; - case Token_GtEq: v.val_f64 = l.val_f64 >= r.val_f64; break; - } - } else if (is_type_string(t)) { - Array args = {}; - array_init(&args, vm->stack_allocator, 2); - array_add(&args, l); - array_add(&args, r); - switch (bo->op) { - case Token_CmpEq: v = vm_call_proc_by_name(vm, make_string("__string_eq"), args); break; - case Token_NotEq: v = vm_call_proc_by_name(vm, make_string("__string_ne"), args); break; - case Token_Lt: v = vm_call_proc_by_name(vm, make_string("__string_lt"), args); break; - case Token_Gt: v = vm_call_proc_by_name(vm, make_string("__string_gt"), args); break; - case Token_LtEq: v = vm_call_proc_by_name(vm, make_string("__string_le"), args); break; - case Token_GtEq: v = vm_call_proc_by_name(vm, make_string("__string_ge"), args); break; - } - } else { - GB_PANIC("TODO(bill): Vector BinaryOp"); - } - - vm_set_value(f, value, v); - } 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; - - // TODO(bill): Take into account size of integer and signedness - 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)) { - if (t == t_f32) { - switch (bo->op) { - case Token_Add: v.val_f32 = l.val_f32 + r.val_f32; break; - case Token_Sub: v.val_f32 = l.val_f32 - r.val_f32; break; - case Token_Mul: v.val_f32 = l.val_f32 * r.val_f32; break; - case Token_Quo: v.val_f32 = l.val_f32 / r.val_f32; break; - - case Token_Mod: GB_PANIC("TODO(bill): BinaryOp f32 Token_Mod"); break; - } - } else if (t == t_f64) { - switch (bo->op) { - case Token_Add: v.val_f64 = l.val_f64 + r.val_f64; break; - case Token_Sub: v.val_f64 = l.val_f64 - r.val_f64; break; - case Token_Mul: v.val_f64 = l.val_f64 * r.val_f64; break; - case Token_Quo: v.val_f64 = l.val_f64 / r.val_f64; break; - - case Token_Mod: GB_PANIC("TODO(bill): BinaryOp f64 Token_Mod"); break; - } - } - } 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_proc(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: { - vmValue vector = vm_operand_value(vm, instr->VectorExtractElement.vector); - vmValue index = vm_operand_value(vm, instr->VectorExtractElement.index); - vmValue v = vector.val_comp[index.val_int]; - vm_set_value(f, value, v); - } break; - - case ssaInstr_VectorInsertElement: { - vmValue vector = vm_operand_value(vm, instr->VectorInsertElement.vector); - vmValue elem = vm_operand_value(vm, instr->VectorInsertElement.elem); - vmValue index = vm_operand_value(vm, instr->VectorInsertElement.index); - vector.val_comp[index.val_int] = elem; - } break; - - case ssaInstr_VectorShuffle: { - auto *vs = &instr->VectorShuffle; - vmValue old_vector = vm_operand_value(vm, instr->VectorShuffle.vector); - vmValue new_vector = {}; - array_init_count(&new_vector.val_comp, vm->stack_allocator, vs->index_count); - - for (i32 i = 0; i < vs->index_count; i++) { - new_vector.val_comp[i] = old_vector.val_comp[vs->indices[i]]; - } - - vm_set_value(f, value, new_vector); - } break; - - case ssaInstr_BoundsCheck: { - auto *bc = &instr->BoundsCheck; - Array args = {}; - array_init(&args, vm->stack_allocator, 5); - array_add(&args, vm_exact_value(vm, NULL, make_exact_value_string(bc->pos.file), t_string)); - array_add(&args, vm_exact_value(vm, NULL, make_exact_value_integer(bc->pos.line), t_int)); - array_add(&args, vm_exact_value(vm, NULL, make_exact_value_integer(bc->pos.column), t_int)); - array_add(&args, vm_operand_value(vm, bc->index)); - array_add(&args, vm_operand_value(vm, bc->len)); - - vm_call_proc_by_name(vm, make_string("__bounds_check_error"), args); - } break; - - case ssaInstr_SliceBoundsCheck: { - auto *bc = &instr->SliceBoundsCheck; - Array args = {}; - - array_init(&args, vm->stack_allocator, 7); - array_add(&args, vm_exact_value(vm, NULL, make_exact_value_string(bc->pos.file), t_string)); - array_add(&args, vm_exact_value(vm, NULL, make_exact_value_integer(bc->pos.line), t_int)); - array_add(&args, vm_exact_value(vm, NULL, make_exact_value_integer(bc->pos.column), t_int)); - array_add(&args, vm_operand_value(vm, bc->low)); - array_add(&args, vm_operand_value(vm, bc->high)); - if (!bc->is_substring) { - array_add(&args, vm_operand_value(vm, bc->max)); - vm_call_proc_by_name(vm, make_string("__slice_expr_error"), args); - } else { - vm_call_proc_by_name(vm, make_string("__substring_expr_error"), args); - } - } break; - - default: { - GB_PANIC(" %d\n", instr->kind); - } break; - } -} - - - -void vm_print_value(vmValue value, Type *type) { - type = base_type(type); - if (is_type_string(type)) { - vmValue data = value.val_comp[0]; - vmValue count = value.val_comp[1]; - gb_printf("`%.*s`", cast(isize)count.val_int, cast(u8 *)data.val_ptr); - } else if (is_type_boolean(type)) { - if (value.val_int != 0) { - gb_printf("true"); - } else { - gb_printf("false"); - } - } else if (is_type_integer(type)) { - gb_printf("%lld", cast(i64)value.val_int); - } else if (type == t_f32) { - gb_printf("%f", value.val_f32); - } else if (type == t_f64) { - gb_printf("%f", value.val_f64); - } else if (is_type_pointer(type)) { - gb_printf("0x%08x", value.val_ptr); - } else if (is_type_array(type)) { - gb_printf("["); - for_array(i, value.val_comp) { - if (i > 0) { - gb_printf(", "); - } - vm_print_value(value.val_comp[i], type->Array.elem); - } - gb_printf("]"); - } else if (is_type_vector(type)) { - gb_printf("<"); - for_array(i, value.val_comp) { - if (i > 0) { - gb_printf(", "); - } - vm_print_value(value.val_comp[i], type->Vector.elem); - } - gb_printf(">"); - } else if (is_type_slice(type)) { - gb_printf("["); - for_array(i, value.val_comp) { - if (i > 0) { - gb_printf(", "); - } - vm_print_value(value.val_comp[i], type->Slice.elem); - } - gb_printf("]"); - } else if (is_type_maybe(type)) { - if (value.val_comp[1].val_int != 0) { - gb_printf("?"); - vm_print_value(value.val_comp[0], type->Maybe.elem); - } else { - gb_printf("nil"); - } - } else if (is_type_struct(type)) { - if (value.val_comp.count == 0) { - gb_printf("nil"); - } else { - gb_printf("{"); - for_array(i, value.val_comp) { - if (i > 0) { - gb_printf(", "); - } - vm_print_value(value.val_comp[i], type->Record.fields[i]->type); - } - gb_printf("}"); - } - } else if (is_type_tuple(type)) { - if (value.val_comp.count != 1) { - gb_printf("("); - } - for_array(i, value.val_comp) { - if (i > 0) { - gb_printf(", "); - } - vm_print_value(value.val_comp[i], type->Tuple.variables[i]->type); - } - if (value.val_comp.count != 1) { - gb_printf(")"); - } - } -}