mirror of
https://github.com/odin-lang/Odin.git
synced 2026-04-30 18:24:00 +00:00
Separate SSA opt; Basic Timings for sections only
This commit is contained in:
@@ -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");
|
||||
|
||||
42
src/main.cpp
42
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<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);
|
||||
}
|
||||
|
||||
141
src/ssa.cpp
141
src/ssa.cpp
@@ -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);
|
||||
}
|
||||
|
||||
// {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
105
src/timings.cpp
Normal 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));
|
||||
}
|
||||
}
|
||||
354
src/vm.cpp
354
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<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
|
||||
|
||||
Reference in New Issue
Block a user