From 7ba0f259433a03ca930dfb55e1927ce1a0c06cf2 Mon Sep 17 00:00:00 2001 From: Ginger Bill Date: Sun, 6 Nov 2016 21:22:05 +0000 Subject: [PATCH] Separate SSA opt; Basic Timings for sections only --- src/array.cpp | 6 +- src/main.cpp | 42 +-- src/ssa.cpp | 141 +++------- src/ssa_opt.cpp | 81 +++--- src/{ssa_to_llvm.cpp => ssa_print.cpp} | 61 ++++- src/timings.cpp | 105 ++++++++ src/vm.cpp | 354 +++++++++++++++++++++++++ 7 files changed, 634 insertions(+), 156 deletions(-) rename src/{ssa_to_llvm.cpp => ssa_print.cpp} (96%) create mode 100644 src/timings.cpp diff --git a/src/array.cpp b/src/array.cpp index 825fdc51e..0f0f3c7de 100644 --- a/src/array.cpp +++ b/src/array.cpp @@ -3,9 +3,9 @@ template struct Array { gbAllocator allocator; - T *data; - isize count; - isize capacity; + T * data; + isize count; + isize capacity; T &operator[](isize index) { GB_ASSERT_MSG(0 <= index && index < count, "Index out of bounds"); diff --git a/src/main.cpp b/src/main.cpp index 7ff6b7eb7..856f9e253 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2,14 +2,16 @@ #include "common.cpp" #include "profiler.cpp" +#include "timings.cpp" #include "unicode.cpp" #include "tokenizer.cpp" #include "parser.cpp" // #include "printer.cpp" #include "checker/checker.cpp" #include "ssa.cpp" -#include "ssa_to_llvm.cpp" -#include "vm.cpp" +#include "ssa_opt.cpp" +#include "ssa_print.cpp" +// #include "vm.cpp" // NOTE(bill): `name` is used in debugging and profiling modes i32 win32_exec_command_line_app(char *name, char *fmt, ...) { @@ -110,6 +112,10 @@ int main(int argc, char **argv) { } prof_init(); + Timings timings = {}; + timings_init(&timings, make_string("Total Time"), 128); + defer (timings_destroy(&timings)); + #if 1 init_string_buffer_memory(); init_global_error_collector(); @@ -136,6 +142,8 @@ int main(int argc, char **argv) { Parser parser = {0}; + timings_start_section(&timings, make_string("Parser")); + if (!init_parser(&parser)) { return 1; } @@ -147,6 +155,8 @@ int main(int argc, char **argv) { #if 1 + timings_start_section(&timings, make_string("Checker")); + Checker checker = {}; ArchData arch_data = make_arch_data(ArchKind_x64); @@ -158,34 +168,26 @@ int main(int argc, char **argv) { #endif #if 1 + ssaGen ssa = {}; if (!ssa_gen_init(&ssa, &checker)) { return 1; } // defer (ssa_gen_destroy(&ssa)); + timings_start_section(&timings, make_string("SSA gen")); ssa_gen_tree(&ssa); -#if 0 - VirtualMachine vm = {}; - vm_init(&vm, &ssa.module); - // defer (vm_destroy(&vm)); + timings_start_section(&timings, make_string("SSA opt")); + ssa_opt_tree(&ssa); - Array main_args = {}; // Empty - vm_call_proc_by_name(&vm, make_string("main"), main_args); -#endif + timings_start_section(&timings, make_string("SSA print")); + ssa_print_llvm_ir(&ssa); - { - ssaFileBuffer buf = {}; - ssa_file_buffer_init(&buf, &ssa.output_file); - defer (ssa_file_buffer_destroy(&buf)); - - ssa_print_llvm_ir(&buf, &ssa.module); - } - - prof_print_all(); + // prof_print_all(); #if 1 + timings_start_section(&timings, make_string("llvm-opt")); char const *output_name = ssa.output_file.filename; isize base_name_len = gb_path_extension(output_name)-1 - output_name; @@ -212,6 +214,7 @@ int main(int argc, char **argv) { } #if 1 + timings_start_section(&timings, make_string("llvm-llc")); // For more arguments: http://llvm.org/docs/CommandGuide/llc.html exit_code = win32_exec_command_line_app("llvm-llc", "%.*sbin/llc %.*s.bc -filetype=obj -O%d " @@ -226,6 +229,7 @@ int main(int argc, char **argv) { return exit_code; } + timings_start_section(&timings, make_string("msvc-link")); gbString lib_str = gb_string_make(heap_allocator(), "Kernel32.lib"); // defer (gb_string_free(lib_str)); @@ -250,6 +254,8 @@ int main(int argc, char **argv) { } // prof_print_all(); + timings_print_all(&timings); + if (run_output) { win32_exec_command_line_app("odin run", "%.*s.exe", cast(int)base_name_len, output_name); } diff --git a/src/ssa.cpp b/src/ssa.cpp index 58028850e..6d0969e46 100644 --- a/src/ssa.cpp +++ b/src/ssa.cpp @@ -19,15 +19,16 @@ struct ssaModule { // String triple; - Map min_dep_map; // Key: Entity * - Map values; // Key: Entity * - Map members; // Key: String - Map type_names; // Key: Type * - Map debug_info; // Key: Unique pointer - i32 global_string_index; - i32 global_array_index; // For ConstantSlice + Map min_dep_map; // Key: Entity * + Map values; // Key: Entity * + Map members; // Key: String + Map type_names; // Key: Type * + Map debug_info; // Key: Unique pointer + i32 global_string_index; + i32 global_array_index; // For ConstantSlice - Array procs; // NOTE(bill): Procedures to generate + Array procs; // NOTE(bill): All procedures with bodies + Array procs_to_generate; // NOTE(bill): Procedures to generate }; // NOTE(bill): For more info, see https://en.wikipedia.org/wiki/Dominator_(graph_theory) @@ -524,60 +525,12 @@ struct ssaDebugInfo { }; }; - - -struct ssaFileBuffer { - gbVirtualMemory vm; - isize offset; - gbFile *output; +struct ssaGen { + ssaModule module; + gbFile output_file; + b32 opt_called; }; -void ssa_file_buffer_init(ssaFileBuffer *f, gbFile *output) { - isize size = 8*gb_virtual_memory_page_size(NULL); - f->vm = gb_vm_alloc(NULL, size); - f->offset = 0; - f->output = output; -} - -void ssa_file_buffer_destroy(ssaFileBuffer *f) { - if (f->offset > 0) { - // NOTE(bill): finish writing buffered data - gb_file_write(f->output, f->vm.data, f->offset); - } - - gb_vm_free(f->vm); -} - -void ssa_file_buffer_write(ssaFileBuffer *f, void *data, isize len) { - if (len > f->vm.size) { - gb_file_write(f->output, data, len); - return; - } - - if ((f->vm.size - f->offset) < len) { - gb_file_write(f->output, f->vm.data, f->offset); - f->offset = 0; - } - u8 *cursor = cast(u8 *)f->vm.data + f->offset; - gb_memmove(cursor, data, len); - f->offset += len; -} - - -void ssa_fprintf(ssaFileBuffer *f, char *fmt, ...) { - va_list va; - va_start(va, fmt); - char buf[4096] = {}; - isize len = gb_snprintf_va(buf, gb_size_of(buf), fmt, va); - ssa_file_buffer_write(f, buf, len-1); - va_end(va); -} - - -void ssa_file_write(ssaFileBuffer *f, void *data, isize len) { - ssa_file_buffer_write(f, data, len); -} - ssaValue *ssa_lookup_member(ssaModule *m, String name) { ssaValue **v = map_get(&m->members, hash_string(name)); if (v != NULL) { @@ -3867,7 +3820,7 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) { ssa_module_add_value(proc->module, e, value); array_add(&proc->children, &value->Proc); - array_add(&proc->module->procs, value); + array_add(&proc->module->procs_to_generate, value); } else { auto *info = proc->module->info; @@ -4429,24 +4382,33 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) { - -//////////////////////////////////////////////////////////////// -// -// @Optimizations -// -//////////////////////////////////////////////////////////////// - -#include "ssa_opt.cpp" - - - //////////////////////////////////////////////////////////////// // // @Procedure // //////////////////////////////////////////////////////////////// +void ssa_number_proc_registers(ssaProcedure *proc) { + i32 reg_index = 0; + for_array(i, proc->blocks) { + ssaBlock *b = proc->blocks[i]; + b->index = i; + for_array(j, b->instrs) { + ssaValue *value = b->instrs[j]; + GB_ASSERT(value->kind == ssaValue_Instr); + ssaInstr *instr = &value->Instr; + if (ssa_instr_type(instr) == NULL) { // NOTE(bill): Ignore non-returning instructions + continue; + } + value->index = reg_index; + reg_index++; + } + } +} + void ssa_begin_procedure_body(ssaProcedure *proc) { + array_add(&proc->module->procs, proc); + array_init(&proc->blocks, heap_allocator()); array_init(&proc->defer_stmts, heap_allocator()); array_init(&proc->children, heap_allocator()); @@ -4478,24 +4440,7 @@ void ssa_end_procedure_body(ssaProcedure *proc) { proc->curr_block = proc->decl_block; ssa_emit_jump(proc, proc->entry_block); - ssa_opt_proc(proc); - -// Number registers - i32 reg_index = 0; - for_array(i, proc->blocks) { - ssaBlock *b = proc->blocks[i]; - b->index = i; - for_array(j, b->instrs) { - ssaValue *value = b->instrs[j]; - GB_ASSERT(value->kind == ssaValue_Instr); - ssaInstr *instr = &value->Instr; - if (ssa_instr_type(instr) == NULL) { // NOTE(bill): Ignore non-returning instructions - continue; - } - value->index = reg_index; - reg_index++; - } - } + ssa_number_proc_registers(proc); } @@ -4592,7 +4537,8 @@ void ssa_init_module(ssaModule *m, Checker *c) { map_init(&m->members, heap_allocator()); map_init(&m->debug_info, heap_allocator()); map_init(&m->type_names, heap_allocator()); - array_init(&m->procs, heap_allocator()); + array_init(&m->procs, heap_allocator()); + array_init(&m->procs_to_generate, heap_allocator()); // Default states m->stmt_state_flags = 0; @@ -4656,7 +4602,7 @@ void ssa_destroy_module(ssaModule *m) { map_destroy(&m->members); map_destroy(&m->type_names); map_destroy(&m->debug_info); - array_free(&m->procs); + array_free(&m->procs_to_generate); gb_arena_free(&m->arena); } @@ -4669,13 +4615,6 @@ void ssa_destroy_module(ssaModule *m) { //////////////////////////////////////////////////////////////// - -struct ssaGen { - ssaModule module; - gbFile output_file; -}; - - b32 ssa_gen_init(ssaGen *s, Checker *c) { if (global_error_collector.count != 0) { return false; @@ -5335,8 +5274,8 @@ void ssa_gen_tree(ssaGen *s) { ssa_end_procedure_body(proc); } - for_array(i, m->procs) { - ssa_build_proc(m->procs[i], m->procs[i]->Proc.parent); + for_array(i, m->procs_to_generate) { + ssa_build_proc(m->procs_to_generate[i], m->procs_to_generate[i]->Proc.parent); } // { diff --git a/src/ssa_opt.cpp b/src/ssa_opt.cpp index 48a92494b..0fb4bd873 100644 --- a/src/ssa_opt.cpp +++ b/src/ssa_opt.cpp @@ -97,7 +97,7 @@ void ssa_opt_add_operands(Array *ops, ssaInstr *i) { -void ssa_block_replace_pred(ssaBlock *b, ssaBlock *from, ssaBlock *to) { +void ssa_opt_block_replace_pred(ssaBlock *b, ssaBlock *from, ssaBlock *to) { for_array(i, b->preds) { ssaBlock *pred = b->preds[i]; if (pred == from) { @@ -106,7 +106,7 @@ void ssa_block_replace_pred(ssaBlock *b, ssaBlock *from, ssaBlock *to) { } } -void ssa_block_replace_succ(ssaBlock *b, ssaBlock *from, ssaBlock *to) { +void ssa_opt_block_replace_succ(ssaBlock *b, ssaBlock *from, ssaBlock *to) { for_array(i, b->succs) { ssaBlock *succ = b->succs[i]; if (succ == from) { @@ -115,7 +115,7 @@ void ssa_block_replace_succ(ssaBlock *b, ssaBlock *from, ssaBlock *to) { } } -b32 ssa_block_has_phi(ssaBlock *b) { +b32 ssa_opt_block_has_phi(ssaBlock *b) { return b->instrs[0]->Instr.kind == ssaInstr_Phi; } @@ -224,7 +224,7 @@ b32 ssa_opt_block_fusion(ssaProcedure *proc, ssaBlock *a) { return false; } - if (ssa_block_has_phi(b)) { + if (ssa_opt_block_has_phi(b)) { return false; } @@ -241,7 +241,7 @@ b32 ssa_opt_block_fusion(ssaProcedure *proc, ssaBlock *a) { // Fix preds links for_array(i, b->succs) { - ssa_block_replace_pred(b->succs[i], b, a); + ssa_opt_block_replace_pred(b->succs[i], b, a); } proc->blocks[b->index] = NULL; @@ -346,18 +346,22 @@ ssaBlock *ssa_lt_eval(ssaLTState *lt, ssaBlock *v) { return u; } -void ssa_number_dom_tree(ssaBlock *v, i32 pre, i32 post, i32 *pre_out, i32 *post_out) { +struct ssaDomPrePost { + i32 pre, post; +}; + +ssaDomPrePost ssa_opt_number_dom_tree(ssaBlock *v, i32 pre, i32 post) { + ssaDomPrePost result = {pre, post}; + v->dom.pre = pre++; for_array(i, v->dom.children) { - ssaBlock *child = v->dom.children[i]; - i32 new_pre = 0, new_post = 0; - ssa_number_dom_tree(child, pre, post, &new_pre, &new_post); - pre = new_pre; - post = new_post; + result = ssa_opt_number_dom_tree(v->dom.children[i], result.pre, result.post); } v->dom.post = post++; - *pre_out = pre; - *post_out = post; + + result.pre = pre; + result.post = post; + return result; } @@ -445,9 +449,7 @@ void ssa_opt_build_dom_tree(ssaProcedure *proc) { } } - i32 pre = 0; - i32 pos = 0; - ssa_number_dom_tree(root, 0, 0, &pre, &pos); + ssa_opt_number_dom_tree(root, 0, 0); } void ssa_opt_mem2reg(ssaProcedure *proc) { @@ -455,23 +457,36 @@ void ssa_opt_mem2reg(ssaProcedure *proc) { } -void ssa_opt_proc(ssaProcedure *proc) { - ssa_opt_blocks(proc); -#if 1 - ssa_opt_build_referrers(proc); - ssa_opt_build_dom_tree(proc); - // TODO(bill): ssa optimization - // [ ] cse (common-subexpression) elim - // [ ] copy elim - // [ ] dead code elim - // [ ] dead store/load elim - // [ ] phi elim - // [ ] short circuit elim - // [ ] bounds check elim - // [ ] lift/mem2reg - // [ ] lift/mem2reg +void ssa_opt_tree(ssaGen *s) { + s->opt_called = true; - ssa_opt_mem2reg(proc); -#endif + for_array(member_index, s->module.procs) { + ssaProcedure *proc = s->module.procs[member_index]; + if (proc->blocks.count == 0) { // Prototype/external procedure + continue; + } + + ssa_opt_blocks(proc); + #if 1 + ssa_opt_build_referrers(proc); + ssa_opt_build_dom_tree(proc); + + // TODO(bill): ssa optimization + // [ ] cse (common-subexpression) elim + // [ ] copy elim + // [ ] dead code elim + // [ ] dead store/load elim + // [ ] phi elim + // [ ] short circuit elim + // [ ] bounds check elim + // [ ] lift/mem2reg + // [ ] lift/mem2reg + + ssa_opt_mem2reg(proc); + #endif + + GB_ASSERT(proc->blocks.count > 0); + ssa_number_proc_registers(proc); + } } diff --git a/src/ssa_to_llvm.cpp b/src/ssa_print.cpp similarity index 96% rename from src/ssa_to_llvm.cpp rename to src/ssa_print.cpp index 9d0e69eda..3c75d8212 100644 --- a/src/ssa_to_llvm.cpp +++ b/src/ssa_print.cpp @@ -1,3 +1,56 @@ +struct ssaFileBuffer { + gbVirtualMemory vm; + isize offset; + gbFile * output; +}; + +void ssa_file_buffer_init(ssaFileBuffer *f, gbFile *output) { + isize size = 8*gb_virtual_memory_page_size(NULL); + f->vm = gb_vm_alloc(NULL, size); + f->offset = 0; + f->output = output; +} + +void ssa_file_buffer_destroy(ssaFileBuffer *f) { + if (f->offset > 0) { + // NOTE(bill): finish writing buffered data + gb_file_write(f->output, f->vm.data, f->offset); + } + + gb_vm_free(f->vm); +} + +void ssa_file_buffer_write(ssaFileBuffer *f, void *data, isize len) { + if (len > f->vm.size) { + gb_file_write(f->output, data, len); + return; + } + + if ((f->vm.size - f->offset) < len) { + gb_file_write(f->output, f->vm.data, f->offset); + f->offset = 0; + } + u8 *cursor = cast(u8 *)f->vm.data + f->offset; + gb_memmove(cursor, data, len); + f->offset += len; +} + + +void ssa_fprintf(ssaFileBuffer *f, char *fmt, ...) { + va_list va; + va_start(va, fmt); + char buf[4096] = {}; + isize len = gb_snprintf_va(buf, gb_size_of(buf), fmt, va); + ssa_file_buffer_write(f, buf, len-1); + va_end(va); +} + + +void ssa_file_write(ssaFileBuffer *f, void *data, isize len) { + ssa_file_buffer_write(f, data, len); +} + + b32 ssa_valid_char(u8 c) { if (c >= 0x80) { return false; @@ -1149,7 +1202,13 @@ void ssa_print_type_name(ssaFileBuffer *f, ssaModule *m, ssaValue *v) { ssa_fprintf(f, "\n"); } -void ssa_print_llvm_ir(ssaFileBuffer *f, ssaModule *m) { +void ssa_print_llvm_ir(ssaGen *ssa) { + ssaModule *m = &ssa->module; + ssaFileBuffer buf = {}, *f = &buf; + ssa_file_buffer_init(f, &ssa->output_file); + defer (ssa_file_buffer_destroy(f)); + + if (m->layout.len > 0) { ssa_fprintf(f, "target datalayout = \"%.*s\"\n", LIT(m->layout)); } diff --git a/src/timings.cpp b/src/timings.cpp new file mode 100644 index 000000000..8de2164fe --- /dev/null +++ b/src/timings.cpp @@ -0,0 +1,105 @@ +struct TimeStamp { + u64 start; + u64 finish; + String label; +}; + +struct Timings { + TimeStamp total; + Array sections; + u64 freq; +}; + + +u64 win32_time_stamp_time_now(void) { + LARGE_INTEGER counter; + QueryPerformanceCounter(&counter); + return counter.QuadPart; +} + +u64 win32_time_stamp__freq(void) { + gb_local_persist LARGE_INTEGER win32_perf_count_freq = {0}; + if (!win32_perf_count_freq.QuadPart) { + QueryPerformanceFrequency(&win32_perf_count_freq); + GB_ASSERT(win32_perf_count_freq.QuadPart != 0); + } + + return win32_perf_count_freq.QuadPart; +} + +u64 time_stamp_time_now(void) { +#if defined(GB_SYSTEM_WINDOWS) + return win32_time_stamp_time_now(); +#else +#error time_stamp_time_now +#endif +} + +u64 time_stamp__freq(void) { +#if defined(GB_SYSTEM_WINDOWS) + return win32_time_stamp__freq(); +#else +#error time_stamp__freq +#endif +} + +TimeStamp make_time_stamp(String label) { + TimeStamp ts = {}; + ts.start = time_stamp_time_now(); + ts.label = label; + return ts; +} + +void timings_init(Timings *t, String label, isize buffer_size) { + array_init(&t->sections, heap_allocator(), buffer_size); + t->total = make_time_stamp(label); + t->freq = time_stamp__freq(); +} + +void timings_destroy(Timings *t) { + array_free(&t->sections); +} + +void timings__stop_current_section(Timings *t) { + if (t->sections.count > 0) { + t->sections[t->sections.count-1].finish = time_stamp_time_now(); + } +} + +void timings_start_section(Timings *t, String label) { + timings__stop_current_section(t); + array_add(&t->sections, make_time_stamp(label)); +} + +f64 time_stamp_as_ms(TimeStamp ts, u64 freq) { + GB_ASSERT_MSG(ts.finish >= ts.start, "time_stamp_as_ms - %.*s", LIT(ts.label)); + return 1000.0 * cast(f64)(ts.finish - ts.start) / cast(f64)freq; +} + +void timings_print_all(Timings *t) { + timings__stop_current_section(t); + t->total.finish = time_stamp_time_now(); + + char const SPACES[] = " "; + + isize max_len = t->total.label.len; + for_array(i, t->sections) { + TimeStamp ts = t->sections[i]; + max_len = gb_max(max_len, ts.label.len); + } + + GB_ASSERT(max_len <= gb_size_of(SPACES)-1); + + gb_printf("%.*s%.*s - %.3f ms\n", + LIT(t->total.label), + cast(int)(max_len-t->total.label.len), SPACES, + time_stamp_as_ms(t->total, t->freq)); + + for_array(i, t->sections) { + TimeStamp ts = t->sections[i]; + gb_printf("%.*s%.*s - %.3f ms\n", + LIT(ts.label), + cast(int)(max_len-ts.label.len), SPACES, + time_stamp_as_ms(ts, t->freq)); + } +} diff --git a/src/vm.cpp b/src/vm.cpp index a3f1f7f98..db9941553 100644 --- a/src/vm.cpp +++ b/src/vm.cpp @@ -1,2 +1,356 @@ +#if 0 // TODO(bill): COMPLETELY REWORK THIS ENTIRE INTERPRETER #include "dyncall/include/dyncall.h" + +struct vmInterpreter; + +/* +Types: +boolean +integer +float +pointer +string +any +array +vector +slice +maybe +struct +union +raw_union +enum +tuple +proc +*/ + +struct vmProcedure { + Type * type; + String name; + b32 is_external; +}; + +struct vmValue { + void *data; + i32 id; + Type *type; + union { + i64 v_int; + f32 v_f32; + f64 v_f64; + vmProcedure * v_proc; + }; +}; + +Array vm_empty_args = {}; + +struct vmFrame { + vmInterpreter *i; + vmFrame * caller; + ssaProcedure * proc; + ssaBlock * block; + ssaBlock * prev_block; + isize instr_index; // For the current block + + Array env; // Index == instr id + vmValue result; +}; + +struct vmInterpreter { + ssaModule * module; + BaseTypeSizes sizes; + gbArena stack_arena; + gbAllocator stack_allocator; + gbAllocator heap_allocator; + + Array frame_stack; + Map globals; +}; + +enum vmContinuation { + vmContinuation_Next, + vmContinuation_Return, + vmContinuation_Branch, +}; + + + + +i64 vm_size_of(vmInterpreter *i, Type *type) { + return type_size_of(i->sizes, i->heap_allocator, type); +} +i64 vm_align_of(vmInterpreter *i, Type *type) { + return type_align_of(i->sizes, i->heap_allocator, type); +} +i64 vm_offset_of(vmInterpreter *i, Type *type, i64 index) { + return type_offset_of(i->sizes, i->heap_allocator, type, index); +} + + + + + + +Array vm_prepare_call(vmFrame *f, ssaInstr *instr, vmValue *proc) { + GB_ASSERT(instr->kind == ssaInstr_Call); + + *proc = vm_get_value(f, instr->Call.value); + + Array args = {}; + array_init_count(&args, f->i->stack_allocator, instr->Call.arg_count); + + for (isize i = 0; i < instr->Call.arg_count; i++) { + args[i] = vm_get_value(f, instr->Call.args[i]); + } + + return args; +} + + +vmContinuation vm_visit_instr(vmFrame *f, ssaValue *value) { + ssaInstr *instr = &value->Instr; +#if 1 + if (instr->kind != ssaInstr_Comment) { + gb_printf("instr: %.*s\n", LIT(ssa_instr_strings[instr->kind])); + } +#endif + switch (instr->kind) { + case ssaInstr_StartupRuntime: { + + } break; + + case ssaInstr_Comment: break; + + case ssaInstr_Local: { + Type *type = ssa_type(value); + GB_ASSERT(is_type_pointer(type)); + i64 size = gb_max(1, vm_size_of(f->i, type)); + i64 align = gb_max(1, vm_align_of(f->i, type)); + void *mem = gb_alloc_align(f->i->stack_allocator, size, align); + + array_add(&f->locals, mem); + } break; + + case ssaInstr_ZeroInit: { + Type *pt = ssa_type(instr->ZeroInit.address); + GB_ASSERT(is_type_pointer(pt)); + vmValue addr = vm_get_value(f, instr->ZeroInit.address); + GB_ASSERT(are_types_identical(addr.type, ptr)); + i64 size = vm_size_of(vm, type_deref(pt)); + gb_zero(addr.v_ptr, size); + } break; + + case ssaInstr_Store: { + ssaValue *addr = instr->Store.Address; + ssaValue *value = instr->Store.Value; + } break; + + case ssaInstr_Load: { + ssaValue *addr = instr->Load.Address; + } break; + + case ssaInstr_ArrayElementPtr: { + + } break; + + case ssaInstr_StructElementPtr: { + + } break; + + case ssaInstr_PtrOffset: { + + } break; + + case ssaInstr_Phi: + for_array(i, f->block->preds) { + ssaBlock *pred = f->block->preds[i]; + if (f->prev_block == pred) { + vmValue edge = vm_get_value(f, instr->Phi.edges[i]); + // vm_set_value(f, value, edge); + break; + } + } + break; + + case ssaInstr_ArrayExtractValue: { + + } break; + + case ssaInstr_StructExtractValue: { + + } break; + + case ssaInstr_Jump: + f->prev_block = f->block; + f->block = instr->Jump.block; + return vmContinuation_Branch; + + case ssaInstr_If: + f->prev_block = f->block; + if (vm_get_value(f, instr->If.cond).v_int != 0) { + f->block = instr->If.true_block; + } else { + f->block = instr->If.false_block; + } + return vmContinuation_Branch; + + case ssaInstr_Return: + if (instr->Return.value != NULL) { + Type *type = base_type(ssa_type(instr->Return.value)); + GB_ASSERT(is_type_tuple(type)); + f->result = vm_get_value(f, instr->Return.value); + if (type->Tuple.variable_count == 1) { + f->result.type = type->Tuple.variables[0]->type; + } + } + f->block = NULL; + return vmContinuation_Return; + + case ssaInstr_Conv: { + + } break; + + case ssaInstr_Unreachable: { + GB_PANIC("Unreachable"); + } break; + + case ssaInstr_BinaryOp: { + + } break; + + case ssaInstr_Call: { + + } break; + + case ssaInstr_Select: { + + } 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; + } + + return vmContinuation_Next; +} + + +void vm_run_frame(vmFrame *f) { + for (;;) { + for_array(i, f->block->instrs) { + ssaValue *v = f->block->instrs[i]; + GB_ASSERT(v->kind == ssaValue_Instr); + switch (vm_visit_instr(f, v)) { + case vmContinuation_Return: + return; + case vmContinuation_Next: + // Do nothing + break; + case vmContinuation_Branch: + goto end; + } + } + end: + ; + } +} + +ssaProcedure *vm_lookup_proc(vmInterpreter *i, String name) { + ssaValue **found = map_get(&i->module->members, hash_string(name)); + if (found == NULL) { + return NULL; + } + ssaValue *v = *found; + if (v->kind != ssaValue_Proc) { + return NULL; + } + + return &v->Proc; +} + +vmValue vm_ext(vmFrame *caller, Array args) { + GB_PANIC("TODO(bill): vm_ext"); + vmValue v = {}; + return v; +} + +vmValue vm_call(vmInterpreter *i, vmFrame *caller, ssaProcedure *proc, Array args) { + if (proc == NULL) { + GB_PANIC("Call to NULL procedure"); + } + + gb_printf("Call: %.*s", LIT(proc->name)); + + vmFrame f = {}; + f.i = i; + f.caller = caller; + f.proc = proc; + if (proc->body == NULL) { + return vm_ext(&f, args); + } + f.block = proc->blocks[0]; + + map_init_with_reserve(&f.env, i->heap_allocator, 1.5*proc->instr_count); + defer (map_destroy(&f.env)); + + array_init_count(&f.locals, i->heap_allocator, proc->local_count); + defer (array_free(&f.locals)); + + for_array(i, proc->params) { + map_set(&f.env, hash_pointer(proc->params[i]), args[i]); + } + + while (f.block != NULL) { + vm_run_frame(&f); + } + + return f.result; +} + +i32 vm_interpret(ssaModule *m) { + i32 exit_code = 2; + + vmInterpreter i = {}; + + i.module = m; + i.sizes = m->sizes; + + gb_arena_init_from_allocator(&i.stack_arena, heap_allocator(), gb_megabytes(64)); + defer (gb_arena_free(&i.stack_arena)); + + i.stack_allocator = gb_arena_allocator(&i.stack_arena); + i.heap_allocator = heap_allocator(); + + ssaProcedure *main_proc = vm_lookup_proc(&i, make_string("main")); + if (main_proc != NULL) { + vm_call(&i, NULL, main_proc, vm_empty_args); + exit_code = 0; + } else { + gb_printf_err("No main procedure."); + exit_code = 1; + } + + return exit_code; +} + +#endif