Browse Source

Separate SSA opt; Basic Timings for sections only

Ginger Bill 8 years ago
parent
commit
7ba0f25943
7 changed files with 637 additions and 159 deletions
  1. 3 3
      src/array.cpp
  2. 24 18
      src/main.cpp
  3. 41 102
      src/ssa.cpp
  4. 50 35
      src/ssa_opt.cpp
  5. 60 1
      src/ssa_print.cpp
  6. 105 0
      src/timings.cpp
  7. 354 0
      src/vm.cpp

+ 3 - 3
src/array.cpp

@@ -3,9 +3,9 @@
 template <typename T>
 template <typename T>
 struct Array {
 struct Array {
 	gbAllocator allocator;
 	gbAllocator allocator;
-	T *data;
-	isize count;
-	isize capacity;
+	T *         data;
+	isize       count;
+	isize       capacity;
 
 
 	T &operator[](isize index) {
 	T &operator[](isize index) {
 		GB_ASSERT_MSG(0 <= index && index < count, "Index out of bounds");
 		GB_ASSERT_MSG(0 <= index && index < count, "Index out of bounds");

+ 24 - 18
src/main.cpp

@@ -2,14 +2,16 @@
 
 
 #include "common.cpp"
 #include "common.cpp"
 #include "profiler.cpp"
 #include "profiler.cpp"
+#include "timings.cpp"
 #include "unicode.cpp"
 #include "unicode.cpp"
 #include "tokenizer.cpp"
 #include "tokenizer.cpp"
 #include "parser.cpp"
 #include "parser.cpp"
 // #include "printer.cpp"
 // #include "printer.cpp"
 #include "checker/checker.cpp"
 #include "checker/checker.cpp"
 #include "ssa.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
 // NOTE(bill): `name` is used in debugging and profiling modes
 i32 win32_exec_command_line_app(char *name, char *fmt, ...) {
 i32 win32_exec_command_line_app(char *name, char *fmt, ...) {
@@ -110,6 +112,10 @@ int main(int argc, char **argv) {
 	}
 	}
 	prof_init();
 	prof_init();
 
 
+	Timings timings = {};
+	timings_init(&timings, make_string("Total Time"), 128);
+	defer (timings_destroy(&timings));
+
 #if 1
 #if 1
 	init_string_buffer_memory();
 	init_string_buffer_memory();
 	init_global_error_collector();
 	init_global_error_collector();
@@ -136,6 +142,8 @@ int main(int argc, char **argv) {
 	Parser parser = {0};
 	Parser parser = {0};
 
 
 
 
+	timings_start_section(&timings, make_string("Parser"));
+
 	if (!init_parser(&parser)) {
 	if (!init_parser(&parser)) {
 		return 1;
 		return 1;
 	}
 	}
@@ -147,6 +155,8 @@ int main(int argc, char **argv) {
 
 
 
 
 #if 1
 #if 1
+	timings_start_section(&timings, make_string("Checker"));
+
 	Checker checker = {};
 	Checker checker = {};
 	ArchData arch_data = make_arch_data(ArchKind_x64);
 	ArchData arch_data = make_arch_data(ArchKind_x64);
 
 
@@ -158,34 +168,26 @@ int main(int argc, char **argv) {
 
 
 #endif
 #endif
 #if 1
 #if 1
+
 	ssaGen ssa = {};
 	ssaGen ssa = {};
 	if (!ssa_gen_init(&ssa, &checker)) {
 	if (!ssa_gen_init(&ssa, &checker)) {
 		return 1;
 		return 1;
 	}
 	}
 	// defer (ssa_gen_destroy(&ssa));
 	// defer (ssa_gen_destroy(&ssa));
 
 
+	timings_start_section(&timings, make_string("SSA gen"));
 	ssa_gen_tree(&ssa);
 	ssa_gen_tree(&ssa);
 
 
-#if 0
-	VirtualMachine vm = {};
-	vm_init(&vm, &ssa.module);
-	// defer (vm_destroy(&vm));
-
-	Array<vmValue> main_args = {}; // Empty
-	vm_call_proc_by_name(&vm, make_string("main"), main_args);
-#endif
-
-	{
-		ssaFileBuffer buf = {};
-		ssa_file_buffer_init(&buf, &ssa.output_file);
-		defer (ssa_file_buffer_destroy(&buf));
+	timings_start_section(&timings, make_string("SSA opt"));
+	ssa_opt_tree(&ssa);
 
 
-		ssa_print_llvm_ir(&buf, &ssa.module);
-	}
+	timings_start_section(&timings, make_string("SSA print"));
+	ssa_print_llvm_ir(&ssa);
 
 
-	prof_print_all();
+	// prof_print_all();
 
 
 #if 1
 #if 1
+	timings_start_section(&timings, make_string("llvm-opt"));
 
 
 	char const *output_name = ssa.output_file.filename;
 	char const *output_name = ssa.output_file.filename;
 	isize base_name_len = gb_path_extension(output_name)-1 - output_name;
 	isize base_name_len = gb_path_extension(output_name)-1 - output_name;
@@ -212,6 +214,7 @@ int main(int argc, char **argv) {
 	}
 	}
 
 
 	#if 1
 	#if 1
+	timings_start_section(&timings, make_string("llvm-llc"));
 	// For more arguments: http://llvm.org/docs/CommandGuide/llc.html
 	// For more arguments: http://llvm.org/docs/CommandGuide/llc.html
 	exit_code = win32_exec_command_line_app("llvm-llc",
 	exit_code = win32_exec_command_line_app("llvm-llc",
 		"%.*sbin/llc %.*s.bc -filetype=obj -O%d "
 		"%.*sbin/llc %.*s.bc -filetype=obj -O%d "
@@ -226,6 +229,7 @@ int main(int argc, char **argv) {
 		return exit_code;
 		return exit_code;
 	}
 	}
 
 
+	timings_start_section(&timings, make_string("msvc-link"));
 
 
 	gbString lib_str = gb_string_make(heap_allocator(), "Kernel32.lib");
 	gbString lib_str = gb_string_make(heap_allocator(), "Kernel32.lib");
 	// defer (gb_string_free(lib_str));
 	// defer (gb_string_free(lib_str));
@@ -250,6 +254,8 @@ int main(int argc, char **argv) {
 	}
 	}
 	// prof_print_all();
 	// prof_print_all();
 
 
+	timings_print_all(&timings);
+
 	if (run_output) {
 	if (run_output) {
 		win32_exec_command_line_app("odin run", "%.*s.exe", cast(int)base_name_len, output_name);
 		win32_exec_command_line_app("odin run", "%.*s.exe", cast(int)base_name_len, output_name);
 	}
 	}

+ 41 - 102
src/ssa.cpp

@@ -19,15 +19,16 @@ struct ssaModule {
 	// String triple;
 	// String triple;
 
 
 
 
-	Map<Entity *>       min_dep_map; // Key: Entity *
-	Map<ssaValue *>     values;      // Key: Entity *
-	Map<ssaValue *>     members;     // Key: String
-	Map<String>         type_names;  // Key: Type *
-	Map<ssaDebugInfo *> debug_info;  // Key: Unique pointer
-	i32                 global_string_index;
-	i32                 global_array_index; // For ConstantSlice
-
-	Array<ssaValue *> procs; // NOTE(bill): Procedures to generate
+	Map<Entity *>         min_dep_map; // Key: Entity *
+	Map<ssaValue *>       values;      // Key: Entity *
+	Map<ssaValue *>       members;     // Key: String
+	Map<String>           type_names;  // Key: Type *
+	Map<ssaDebugInfo *>   debug_info;  // Key: Unique pointer
+	i32                   global_string_index;
+	i32                   global_array_index; // For ConstantSlice
+
+	Array<ssaProcedure *> procs;             // NOTE(bill): All procedures with bodies
+	Array<ssaValue *>     procs_to_generate; // NOTE(bill): Procedures to generate
 };
 };
 
 
 // NOTE(bill): For more info, see https://en.wikipedia.org/wiki/Dominator_(graph_theory)
 // 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 *ssa_lookup_member(ssaModule *m, String name) {
 	ssaValue **v = map_get(&m->members, hash_string(name));
 	ssaValue **v = map_get(&m->members, hash_string(name));
 	if (v != NULL) {
 	if (v != NULL) {
@@ -3867,7 +3820,7 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) {
 
 
 			ssa_module_add_value(proc->module, e, value);
 			ssa_module_add_value(proc->module, e, value);
 			array_add(&proc->children, &value->Proc);
 			array_add(&proc->children, &value->Proc);
-			array_add(&proc->module->procs, value);
+			array_add(&proc->module->procs_to_generate, value);
 		} else {
 		} else {
 			auto *info = proc->module->info;
 			auto *info = proc->module->info;
 
 
@@ -4429,24 +4382,33 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) {
 
 
 
 
 
 
-
-////////////////////////////////////////////////////////////////
-//
-// @Optimizations
-//
-////////////////////////////////////////////////////////////////
-
-#include "ssa_opt.cpp"
-
-
-
 ////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////
 //
 //
 // @Procedure
 // @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) {
 void ssa_begin_procedure_body(ssaProcedure *proc) {
+	array_add(&proc->module->procs, proc);
+
 	array_init(&proc->blocks,      heap_allocator());
 	array_init(&proc->blocks,      heap_allocator());
 	array_init(&proc->defer_stmts, heap_allocator());
 	array_init(&proc->defer_stmts, heap_allocator());
 	array_init(&proc->children,    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;
 	proc->curr_block = proc->decl_block;
 	ssa_emit_jump(proc, proc->entry_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->members,    heap_allocator());
 	map_init(&m->debug_info, heap_allocator());
 	map_init(&m->debug_info, heap_allocator());
 	map_init(&m->type_names, 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
 	// Default states
 	m->stmt_state_flags = 0;
 	m->stmt_state_flags = 0;
@@ -4656,7 +4602,7 @@ void ssa_destroy_module(ssaModule *m) {
 	map_destroy(&m->members);
 	map_destroy(&m->members);
 	map_destroy(&m->type_names);
 	map_destroy(&m->type_names);
 	map_destroy(&m->debug_info);
 	map_destroy(&m->debug_info);
-	array_free(&m->procs);
+	array_free(&m->procs_to_generate);
 	gb_arena_free(&m->arena);
 	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) {
 b32 ssa_gen_init(ssaGen *s, Checker *c) {
 	if (global_error_collector.count != 0) {
 	if (global_error_collector.count != 0) {
 		return false;
 		return false;
@@ -5335,8 +5274,8 @@ void ssa_gen_tree(ssaGen *s) {
 		ssa_end_procedure_body(proc);
 		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);
 	}
 	}
 
 
 	// {
 	// {

+ 50 - 35
src/ssa_opt.cpp

@@ -97,7 +97,7 @@ void ssa_opt_add_operands(Array<ssaValue *> *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) {
 	for_array(i, b->preds) {
 		ssaBlock *pred = b->preds[i];
 		ssaBlock *pred = b->preds[i];
 		if (pred == from) {
 		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) {
 	for_array(i, b->succs) {
 		ssaBlock *succ = b->succs[i];
 		ssaBlock *succ = b->succs[i];
 		if (succ == from) {
 		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;
 	return b->instrs[0]->Instr.kind == ssaInstr_Phi;
 }
 }
 
 
@@ -224,7 +224,7 @@ b32 ssa_opt_block_fusion(ssaProcedure *proc, ssaBlock *a) {
 		return false;
 		return false;
 	}
 	}
 
 
-	if (ssa_block_has_phi(b)) {
+	if (ssa_opt_block_has_phi(b)) {
 		return false;
 		return false;
 	}
 	}
 
 
@@ -241,7 +241,7 @@ b32 ssa_opt_block_fusion(ssaProcedure *proc, ssaBlock *a) {
 
 
 	// Fix preds links
 	// Fix preds links
 	for_array(i, b->succs) {
 	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;
 	proc->blocks[b->index] = NULL;
@@ -346,18 +346,22 @@ ssaBlock *ssa_lt_eval(ssaLTState *lt, ssaBlock *v) {
 	return u;
 	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++;
 	v->dom.pre = pre++;
 	for_array(i, v->dom.children) {
 	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++;
 	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) {
 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
-
-	ssa_opt_mem2reg(proc);
-#endif
+
+void ssa_opt_tree(ssaGen *s) {
+	s->opt_called = true;
+
+	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);
+	}
 }
 }

+ 60 - 1
src/ssa_to_llvm.cpp → 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) {
 b32 ssa_valid_char(u8 c) {
 	if (c >= 0x80) {
 	if (c >= 0x80) {
 		return false;
 		return false;
@@ -1149,7 +1202,13 @@ void ssa_print_type_name(ssaFileBuffer *f, ssaModule *m, ssaValue *v) {
 	ssa_fprintf(f, "\n");
 	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) {
 	if (m->layout.len > 0) {
 		ssa_fprintf(f, "target datalayout = \"%.*s\"\n", LIT(m->layout));
 		ssa_fprintf(f, "target datalayout = \"%.*s\"\n", LIT(m->layout));
 	}
 	}

+ 105 - 0
src/timings.cpp

@@ -0,0 +1,105 @@
+struct TimeStamp {
+	u64    start;
+	u64    finish;
+	String label;
+};
+
+struct Timings {
+	TimeStamp        total;
+	Array<TimeStamp> 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));
+	}
+}

+ 354 - 0
src/vm.cpp

@@ -1,2 +1,356 @@
+#if 0
 // TODO(bill): COMPLETELY REWORK THIS ENTIRE INTERPRETER
 // TODO(bill): COMPLETELY REWORK THIS ENTIRE INTERPRETER
 #include "dyncall/include/dyncall.h"
 #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<vmValue> vm_empty_args = {};
+
+struct vmFrame {
+	vmInterpreter *i;
+	vmFrame *      caller;
+	ssaProcedure * proc;
+	ssaBlock *     block;
+	ssaBlock *     prev_block;
+	isize          instr_index; // For the current block
+
+	Array<void *>  env; // Index == instr id
+	vmValue        result;
+};
+
+struct vmInterpreter {
+	ssaModule *    module;
+	BaseTypeSizes  sizes;
+	gbArena        stack_arena;
+	gbAllocator    stack_allocator;
+	gbAllocator    heap_allocator;
+
+	Array<vmFrame> frame_stack;
+	Map<vmValue>   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<vmValue> vm_prepare_call(vmFrame *f, ssaInstr *instr, vmValue *proc) {
+	GB_ASSERT(instr->kind == ssaInstr_Call);
+
+	*proc = vm_get_value(f, instr->Call.value);
+
+	Array<vmValue> 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("<unknown instr> %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<vmValue> args) {
+	GB_PANIC("TODO(bill): vm_ext");
+	vmValue v = {};
+	return v;
+}
+
+vmValue vm_call(vmInterpreter *i, vmFrame *caller, ssaProcedure *proc, Array<vmValue> 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