Separate SSA opt; Basic Timings for sections only

This commit is contained in:
Ginger Bill
2016-11-06 21:22:05 +00:00
parent 6c2772d093
commit 7ba0f25943
7 changed files with 634 additions and 156 deletions

View File

@@ -3,9 +3,9 @@
template <typename T>
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");

View File

@@ -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<vmValue> 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);
}

View File

@@ -19,15 +19,16 @@ struct ssaModule {
// 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
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
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)
@@ -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);
}
// {

View File

@@ -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) {
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);
}
}

View File

@@ -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));
}

105
src/timings.cpp Normal file
View File

@@ -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));
}
}

View File

@@ -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<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