Browse Source

Very start of working on Tilde Backend for Odin

gingerBill 2 years ago
parent
commit
7cd2d14b64
5 changed files with 927 additions and 40 deletions
  1. 8 4
      src/entity.cpp
  2. 2 0
      src/llvm_backend.hpp
  3. 7 1
      src/main.cpp
  4. 750 35
      src/tilde_backend.cpp
  5. 160 0
      src/tilde_backend.hpp

+ 8 - 4
src/entity.cpp

@@ -2,8 +2,6 @@ struct Scope;
 struct Checker;
 struct Checker;
 struct Type;
 struct Type;
 struct DeclInfo;
 struct DeclInfo;
-struct lbModule;
-struct lbProcedure;
 
 
 
 
 #define ENTITY_KINDS \
 #define ENTITY_KINDS \
@@ -183,8 +181,14 @@ struct Entity {
 
 
 	Entity *    aliased_of;
 	Entity *    aliased_of;
 
 
-	lbModule *   code_gen_module;
-	lbProcedure *code_gen_procedure;
+	union {
+		struct lbModule *code_gen_module;
+		struct cgModule *cg_module;
+	};
+	union {
+		struct lbProcedure *code_gen_procedure;
+		struct cgProcedure *cg_procedure;
+	};
 
 
 	u64         order_in_src;
 	u64         order_in_src;
 	String      deprecated_message;
 	String      deprecated_message;

+ 2 - 0
src/llvm_backend.hpp

@@ -346,7 +346,9 @@ struct lbProcedure {
 };
 };
 
 
 
 
+#ifndef ABI_PKG_NAME_SEPARATOR
 #define ABI_PKG_NAME_SEPARATOR "."
 #define ABI_PKG_NAME_SEPARATOR "."
+#endif
 
 
 
 
 #if !ODIN_LLVM_MINIMUM_VERSION_14
 #if !ODIN_LLVM_MINIMUM_VERSION_14

+ 7 - 1
src/main.cpp

@@ -2897,9 +2897,15 @@ int main(int arg_count, char const **arg_ptr) {
 #if defined(GB_SYSTEM_WINDOWS)
 #if defined(GB_SYSTEM_WINDOWS)
 	if (build_context.tilde_backend) {
 	if (build_context.tilde_backend) {
 		MAIN_TIME_SECTION("Tilde Code Gen");
 		MAIN_TIME_SECTION("Tilde Code Gen");
-		if (!tb_generate_code(checker)) {
+		if (!cg_generate_code(checker)) {
 			return 1;
 			return 1;
 		}
 		}
+
+		if (build_context.show_timings) {
+			show_timings(checker, &global_timings);
+		}
+
+		return 0;
 	} else
 	} else
 #endif
 #endif
 	{
 	{

+ 750 - 35
src/tilde_backend.cpp

@@ -1,23 +1,716 @@
-#if defined(GB_SYSTEM_WINDOWS)
-	#pragma warning(push)
-	#pragma warning(disable: 4200)
-	#pragma warning(disable: 4201)
-	#define restrict gb_restrict
-#endif
+#include "tilde_backend.hpp"
 
 
-#include "tilde/tb.h"
+gb_internal cgValue cg_value(TB_Global *g, Type *type) {
+	return cg_value((TB_Symbol *)g, type);
+}
+gb_internal cgValue cg_value(TB_External *e, Type *type) {
+	return cg_value((TB_Symbol *)e, type);
+}
+gb_internal cgValue cg_value(TB_Function *f, Type *type) {
+	return cg_value((TB_Symbol *)f, type);
+}
+gb_internal cgValue cg_value(TB_Symbol *s, Type *type) {
+	cgValue v = {};
+	v.kind = cgValue_Symbol;
+	v.type = type;
+	v.symbol = s;
+	return v;
+}
+gb_internal cgValue cg_value(TB_Node *node, Type *type) {
+	cgValue v = {};
+	v.kind = cgValue_Value;
+	v.type = type;
+	v.node = node;
+	return v;
+}
+gb_internal cgValue cg_lvalue_addr(TB_Node *node, Type *type) {
+	GB_ASSERT(node->dt.type == TB_PTR);
+	cgValue v = {};
+	v.kind = cgValue_Addr;
+	v.type = type;
+	v.node = node;
+	return v;
+}
 
 
-#if defined(GB_SYSTEM_WINDOWS)
-	#pragma warning(pop)
-#endif
+gb_internal cgAddr cg_addr(cgValue const &value) {
+	cgAddr addr = {};
+	addr.kind = cgAddr_Default;
+	addr.addr = value;
+	if (addr.addr.kind == cgValue_Addr) {
+		GB_ASSERT(addr.addr.node != nullptr);
+		addr.addr.kind = cgValue_Value;
+		addr.addr.type = alloc_type_pointer(addr.addr.type);
+	}
+	return addr;
+}
+
+
+gb_internal void cg_add_entity(cgModule *m, Entity *e, cgValue const &val) {
+	if (e) {
+		rw_mutex_lock(&m->values_mutex);
+		map_set(&m->values, e, val);
+		rw_mutex_unlock(&m->values_mutex);
+	}
+}
+
+gb_internal void cg_add_member(cgModule *m, String const &name, cgValue const &val) {
+	if (name.len > 0) {
+		rw_mutex_lock(&m->values_mutex);
+		string_map_set(&m->members, name, val);
+		rw_mutex_unlock(&m->values_mutex);
+	}
+}
+
+gb_internal void cg_add_procedure_value(cgModule *m, cgProcedure *p) {
+	rw_mutex_lock(&m->values_mutex);
+	if (p->entity != nullptr) {
+		map_set(&m->procedure_values, p->func, p->entity);
+	}
+	string_map_set(&m->procedures, p->name, p);
+	rw_mutex_unlock(&m->values_mutex);
+
+}
+
+gb_internal isize cg_type_info_index(CheckerInfo *info, Type *type, bool err_on_not_found=true) {
+	auto *set = &info->minimum_dependency_type_info_set;
+	isize index = type_info_index(info, type, err_on_not_found);
+	if (index >= 0) {
+		auto *found = map_get(set, index);
+		if (found) {
+			GB_ASSERT(*found >= 0);
+			return *found + 1;
+		}
+	}
+	if (err_on_not_found) {
+		GB_PANIC("NOT FOUND lb_type_info_index %s @ index %td", type_to_string(type), index);
+	}
+	return -1;
+}
+
+gb_internal void cg_create_global_variables(cgModule *m) {
+	if (build_context.no_rtti) {
+		return;
+	}
+
+	CheckerInfo *info = m->info;
+	{ // Add type info data
+		isize max_type_info_count = info->minimum_dependency_type_info_set.count+1;
+		// gb_printf_err("max_type_info_count: %td\n", max_type_info_count);
+		Type *t = alloc_type_array(t_type_info, max_type_info_count);
+
+		TB_Global *g = tb_global_create(m->mod, CG_TYPE_INFO_DATA_NAME, nullptr, TB_LINKAGE_PRIVATE);
+		tb_global_set_storage(m->mod, tb_module_get_rdata(m->mod), g, type_size_of(t), 16, max_type_info_count);
+
+		cgValue value = cg_value(g, alloc_type_pointer(t));
+		cg_global_type_info_data_entity = alloc_entity_variable(nullptr, make_token_ident(CG_TYPE_INFO_DATA_NAME), t, EntityState_Resolved);
+		cg_add_entity(m, cg_global_type_info_data_entity, value);
+	}
+
+	{ // Type info member buffer
+		// NOTE(bill): Removes need for heap allocation by making it global memory
+		isize count = 0;
+
+		for (Type *t : m->info->type_info_types) {
+			isize index = cg_type_info_index(m->info, t, false);
+			if (index < 0) {
+				continue;
+			}
+
+			switch (t->kind) {
+			case Type_Union:
+				count += t->Union.variants.count;
+				break;
+			case Type_Struct:
+				count += t->Struct.fields.count;
+				break;
+			case Type_Tuple:
+				count += t->Tuple.variables.count;
+				break;
+			}
+		}
+
+		if (count > 0) {
+			{
+				char const *name = CG_TYPE_INFO_TYPES_NAME;
+				Type *t = alloc_type_array(t_type_info_ptr, count);
+				TB_Global *g = tb_global_create(m->mod, name, nullptr, TB_LINKAGE_PRIVATE);
+				tb_global_set_storage(m->mod, tb_module_get_rdata(m->mod), g, type_size_of(t), 16, count);
+				cg_global_type_info_member_types = cg_addr(cg_value(g, alloc_type_pointer(t)));
+
+			}
+			{
+				char const *name = CG_TYPE_INFO_NAMES_NAME;
+				Type *t = alloc_type_array(t_string, count);
+				TB_Global *g = tb_global_create(m->mod, name, nullptr, TB_LINKAGE_PRIVATE);
+				tb_global_set_storage(m->mod, tb_module_get_rdata(m->mod), g, type_size_of(t), 16, count);
+				cg_global_type_info_member_names = cg_addr(cg_value(g, alloc_type_pointer(t)));
+			}
+			{
+				char const *name = CG_TYPE_INFO_OFFSETS_NAME;
+				Type *t = alloc_type_array(t_uintptr, count);
+				TB_Global *g = tb_global_create(m->mod, name, nullptr, TB_LINKAGE_PRIVATE);
+				tb_global_set_storage(m->mod, tb_module_get_rdata(m->mod), g, type_size_of(t), 16, count);
+				cg_global_type_info_member_offsets = cg_addr(cg_value(g, alloc_type_pointer(t)));
+			}
+
+			{
+				char const *name = CG_TYPE_INFO_USINGS_NAME;
+				Type *t = alloc_type_array(t_bool, count);
+				TB_Global *g = tb_global_create(m->mod, name, nullptr, TB_LINKAGE_PRIVATE);
+				tb_global_set_storage(m->mod, tb_module_get_rdata(m->mod), g, type_size_of(t), 16, count);
+				cg_global_type_info_member_usings = cg_addr(cg_value(g, alloc_type_pointer(t)));
+			}
+
+			{
+				char const *name = CG_TYPE_INFO_TAGS_NAME;
+				Type *t = alloc_type_array(t_string, count);
+				TB_Global *g = tb_global_create(m->mod, name, nullptr, TB_LINKAGE_PRIVATE);
+				tb_global_set_storage(m->mod, tb_module_get_rdata(m->mod), g, type_size_of(t), 16, count);
+				cg_global_type_info_member_tags = cg_addr(cg_value(g, alloc_type_pointer(t)));
+			}
+		}
+	}
+}
+
+cgModule *cg_module_create(Checker *c) {
+	cgModule *m = gb_alloc_item(permanent_allocator(), cgModule);
+
+	m->checker = c;
+	m->info = &c->info;
 
 
 
 
-bool tb_generate_code(Checker *c) {
 	TB_FeatureSet feature_set = {};
 	TB_FeatureSet feature_set = {};
 	bool is_jit = false;
 	bool is_jit = false;
-	TB_Module *m = tb_module_create(TB_ARCH_X86_64, TB_SYSTEM_WINDOWS, &feature_set, is_jit);
-	defer (tb_module_destroy(m));
-	tb_module_set_tls_index(m, "_tls_index");
+	m->mod = tb_module_create(TB_ARCH_X86_64, TB_SYSTEM_WINDOWS, &feature_set, is_jit);
+	tb_module_set_tls_index(m->mod, "_tls_index");
+
+	map_init(&m->values);
+	array_init(&m->procedures_to_generate, heap_allocator());
+
+	return m;
+}
+
+void cg_module_destroy(cgModule *m) {
+	map_destroy(&m->values);
+	array_free(&m->procedures_to_generate);
+
+	tb_module_destroy(m->mod);
+}
+
+gb_internal String cg_set_nested_type_name_ir_mangled_name(Entity *e, cgProcedure *p) {
+	// NOTE(bill, 2020-03-08): A polymorphic procedure may take a nested type declaration
+	// and as a result, the declaration does not have time to determine what it should be
+
+	GB_ASSERT(e != nullptr && e->kind == Entity_TypeName);
+	if (e->TypeName.ir_mangled_name.len != 0)  {
+		return e->TypeName.ir_mangled_name;
+	}
+	GB_ASSERT((e->scope->flags & ScopeFlag_File) == 0);
+
+	if (p == nullptr) {
+		Entity *proc = nullptr;
+		if (e->parent_proc_decl != nullptr) {
+			proc = e->parent_proc_decl->entity;
+		} else {
+			Scope *scope = e->scope;
+			while (scope != nullptr && (scope->flags & ScopeFlag_Proc) == 0) {
+				scope = scope->parent;
+			}
+			GB_ASSERT(scope != nullptr);
+			GB_ASSERT(scope->flags & ScopeFlag_Proc);
+			proc = scope->procedure_entity;
+		}
+		GB_ASSERT(proc->kind == Entity_Procedure);
+		if (proc->cg_procedure != nullptr) {
+			p = proc->cg_procedure;
+		}
+	}
+
+	// NOTE(bill): Generate a new name
+	// parent_proc.name-guid
+	String ts_name = e->token.string;
+
+	if (p != nullptr) {
+		isize name_len = p->name.len + 1 + ts_name.len + 1 + 10 + 1;
+		char *name_text = gb_alloc_array(permanent_allocator(), char, name_len);
+		u32 guid = 1+p->module->nested_type_name_guid.fetch_add(1);
+		name_len = gb_snprintf(name_text, name_len, "%.*s" ABI_PKG_NAME_SEPARATOR "%.*s-%u", LIT(p->name), LIT(ts_name), guid);
+
+		String name = make_string(cast(u8 *)name_text, name_len-1);
+		e->TypeName.ir_mangled_name = name;
+		return name;
+	} else {
+		// NOTE(bill): a nested type be required before its parameter procedure exists. Just give it a temp name for now
+		isize name_len = 9 + 1 + ts_name.len + 1 + 10 + 1;
+		char *name_text = gb_alloc_array(permanent_allocator(), char, name_len);
+		static std::atomic<u32> guid;
+		name_len = gb_snprintf(name_text, name_len, "_internal" ABI_PKG_NAME_SEPARATOR "%.*s-%u", LIT(ts_name), 1+guid.fetch_add(1));
+
+		String name = make_string(cast(u8 *)name_text, name_len-1);
+		e->TypeName.ir_mangled_name = name;
+		return name;
+	}
+}
+
+gb_internal String cg_mangle_name(cgModule *m, Entity *e) {
+	String name = e->token.string;
+
+	AstPackage *pkg = e->pkg;
+	GB_ASSERT_MSG(pkg != nullptr, "Missing package for '%.*s'", LIT(name));
+	String pkgn = pkg->name;
+	GB_ASSERT(!rune_is_digit(pkgn[0]));
+	if (pkgn == "llvm") {
+		GB_PANIC("llvm. entities are not allowed with the tilde backend");
+	}
+
+	isize max_len = pkgn.len + 1 + name.len + 1;
+	bool require_suffix_id = is_type_polymorphic(e->type, true);
+
+	if ((e->scope->flags & (ScopeFlag_File | ScopeFlag_Pkg)) == 0) {
+		require_suffix_id = true;
+	} else if (is_blank_ident(e->token)) {
+		require_suffix_id = true;
+	}if (e->flags & EntityFlag_NotExported) {
+		require_suffix_id = true;
+	}
+
+	if (require_suffix_id) {
+		max_len += 21;
+	}
+
+	char *new_name = gb_alloc_array(permanent_allocator(), char, max_len);
+	isize new_name_len = gb_snprintf(
+		new_name, max_len,
+		"%.*s" ABI_PKG_NAME_SEPARATOR "%.*s", LIT(pkgn), LIT(name)
+	);
+	if (require_suffix_id) {
+		char *str = new_name + new_name_len-1;
+		isize len = max_len-new_name_len;
+		isize extra = gb_snprintf(str, len, "-%llu", cast(unsigned long long)e->id);
+		new_name_len += extra-1;
+	}
+
+	String mangled_name = make_string((u8 const *)new_name, new_name_len-1);
+	return mangled_name;
+}
+
+String cg_get_entity_name(cgModule *m, Entity *e) {
+	if (e != nullptr && e->kind == Entity_TypeName && e->TypeName.ir_mangled_name.len != 0) {
+		return e->TypeName.ir_mangled_name;
+	}
+	GB_ASSERT(e != nullptr);
+
+	if (e->pkg == nullptr) {
+		return e->token.string;
+	}
+
+	if (e->kind == Entity_TypeName && (e->scope->flags & ScopeFlag_File) == 0) {
+		return cg_set_nested_type_name_ir_mangled_name(e, nullptr);
+	}
+
+	String name = {};
+
+	bool no_name_mangle = false;
+
+	if (e->kind == Entity_Variable) {
+		bool is_foreign = e->Variable.is_foreign;
+		bool is_export  = e->Variable.is_export;
+		no_name_mangle = e->Variable.link_name.len > 0 || is_foreign || is_export;
+		if (e->Variable.link_name.len > 0) {
+			return e->Variable.link_name;
+		}
+	} else if (e->kind == Entity_Procedure && e->Procedure.link_name.len > 0) {
+		return e->Procedure.link_name;
+	} else if (e->kind == Entity_Procedure && e->Procedure.is_export) {
+		no_name_mangle = true;
+	}
+
+	if (!no_name_mangle) {
+		name = cg_mangle_name(m, e);
+	}
+	if (name.len == 0) {
+		name = e->token.string;
+	}
+
+	if (e->kind == Entity_TypeName) {
+		e->TypeName.ir_mangled_name = name;
+	} else if (e->kind == Entity_Procedure) {
+		e->Procedure.link_name = name;
+	}
+
+	return name;
+}
+
+
+struct cgGlobalVariable {
+	cgValue var;
+	cgValue init;
+	DeclInfo *decl;
+	bool is_initialized;
+};
+
+
+gb_internal TB_FunctionPrototype *cg_procedure_type_as_prototype(cgModule *m, Type *type) {
+	GB_ASSERT(type != nullptr);
+	type = base_type(type);
+	GB_ASSERT(type->kind == Type_Proc);
+	TypeProc *pt = &type->Proc;
+
+	auto params = array_make<TB_PrototypeParam>(heap_allocator(), 0, pt->param_count);
+	if (pt->params) for (Entity *e : pt->params->Tuple.variables) {
+		TB_PrototypeParam param = {};
+
+		Type *t = core_type(e->type);
+		i64 sz = type_size_of(t);
+		switch (t->kind) {
+		case Type_Basic:
+			switch (t->Basic.kind) {
+			case Basic_bool:
+			case Basic_b8:
+			case Basic_b16:
+			case Basic_b32:
+			case Basic_b64:
+			case Basic_i8:
+			case Basic_u8:
+			case Basic_i16:
+			case Basic_u16:
+			case Basic_i32:
+			case Basic_u32:
+			case Basic_i64:
+			case Basic_u64:
+			case Basic_i128:
+			case Basic_u128:
+			case Basic_rune:
+			case Basic_int:
+			case Basic_uint:
+			case Basic_uintptr:
+				param.dt = TB_TYPE_INTN(cast(u16)(8*sz));
+				break;
+
+			case Basic_f16: param.dt = TB_TYPE_I16; break;
+			case Basic_f32: param.dt = TB_TYPE_F32; break;
+			case Basic_f64: param.dt = TB_TYPE_F64; break;
+
+			case Basic_complex32:
+			case Basic_complex64:
+			case Basic_complex128:
+			case Basic_quaternion64:
+			case Basic_quaternion128:
+			case Basic_quaternion256:
+				param.dt = TB_TYPE_PTR;
+				break;
+
+
+			case Basic_rawptr:
+				param.dt = TB_TYPE_PTR;
+				break;
+			case Basic_string:  // ^u8 + int
+				param.dt = TB_TYPE_PTR;
+				break;
+			case Basic_cstring: // ^u8
+				param.dt = TB_TYPE_PTR;
+				break;
+			case Basic_any:     // rawptr + ^Type_Info
+				param.dt = TB_TYPE_PTR;
+				break;
+
+			case Basic_typeid:
+				param.dt = TB_TYPE_INTN(cast(u16)(8*sz));
+				break;
+
+			// Endian Specific Types
+			case Basic_i16le:
+			case Basic_u16le:
+			case Basic_i32le:
+			case Basic_u32le:
+			case Basic_i64le:
+			case Basic_u64le:
+			case Basic_i128le:
+			case Basic_u128le:
+			case Basic_i16be:
+			case Basic_u16be:
+			case Basic_i32be:
+			case Basic_u32be:
+			case Basic_i64be:
+			case Basic_u64be:
+			case Basic_i128be:
+			case Basic_u128be:
+				param.dt = TB_TYPE_INTN(cast(u16)(8*sz));
+				break;
+
+			case Basic_f16le: param.dt = TB_TYPE_I16; break;
+			case Basic_f32le: param.dt = TB_TYPE_F32; break;
+			case Basic_f64le: param.dt = TB_TYPE_F64; break;
+
+			case Basic_f16be: param.dt = TB_TYPE_I16; break;
+			case Basic_f32be: param.dt = TB_TYPE_F32; break;
+			case Basic_f64be: param.dt = TB_TYPE_F64; break;
+			}
+
+		}
+
+		if (param.dt.width != 0) {
+			if (is_blank_ident(e->token)) {
+				param.name = alloc_cstring(temporary_allocator(), e->token.string);
+			}
+			array_add(&params, param);
+		}
+	}
+
+	return tb_prototype_create(m->mod, TB_CDECL, params.count, params.data, 0, nullptr, false);
+}
+
+gb_internal cgProcedure *cg_procedure_create(cgModule *m, Entity *entity, bool ignore_body=false) {
+	GB_ASSERT(entity != nullptr);
+	GB_ASSERT(entity->kind == Entity_Procedure);
+	if (!entity->Procedure.is_foreign) {
+		if ((entity->flags & EntityFlag_ProcBodyChecked) == 0) {
+			GB_PANIC("%.*s :: %s (was parapoly: %d %d)", LIT(entity->token.string), type_to_string(entity->type), is_type_polymorphic(entity->type, true), is_type_polymorphic(entity->type, false));
+		}
+	}
+
+	String link_name = cg_get_entity_name(m, entity);
+
+	cgProcedure *p = nullptr;
+	{
+		StringHashKey key = string_hash_string(link_name);
+		cgValue *found = string_map_get(&m->members, key);
+		if (found) {
+			cg_add_entity(m, entity, *found);
+			p = string_map_must_get(&m->procedures, key);
+			if (!ignore_body && p->func != nullptr) {
+				return nullptr;
+			}
+		}
+	}
+
+	if (p == nullptr) {
+		p = gb_alloc_item(permanent_allocator(), cgProcedure);
+	}
+
+	p->module = m;
+	p->entity = entity;
+	p->name = link_name;
+
+	DeclInfo *decl = entity->decl_info;
+
+	ast_node(pl, ProcLit, decl->proc_lit);
+	Type *pt = base_type(entity->type);
+	GB_ASSERT(pt->kind == Type_Proc);
+
+	p->type           = entity->type;
+	p->type_expr      = decl->type_expr;
+	p->body           = pl->body;
+	p->inlining       = pl->inlining;
+	p->is_foreign     = entity->Procedure.is_foreign;
+	p->is_export      = entity->Procedure.is_export;
+	p->is_entry_point = false;
+
+	gbAllocator a = heap_allocator();
+	p->children.allocator      = a;
+	// p->defer_stmts.allocator   = a;
+	// p->blocks.allocator        = a;
+	// p->branch_blocks.allocator = a;
+	// p->context_stack.allocator = a;
+	// p->scope_stack.allocator   = a;
+	// map_init(&p->tuple_fix_map, 0);
+
+	char const *link_name_c = alloc_cstring(temporary_allocator(), link_name);
+
+	TB_Linkage linkage = TB_LINKAGE_PRIVATE;
+	if (p->is_export) {
+		linkage = TB_LINKAGE_PUBLIC;
+	} else if (p->is_foreign || ignore_body) {
+		if (ignore_body) {
+			linkage = TB_LINKAGE_PUBLIC;
+		}
+		p->symbol = cast(TB_Symbol *)tb_extern_create(m->mod, link_name_c, TB_EXTERNAL_SO_LOCAL);
+	}
+
+	if (p->symbol == nullptr)  {
+		p->func = tb_function_create(m->mod, link_name_c, linkage, TB_COMDAT_NONE);
+		tb_function_set_prototype(p->func, cg_procedure_type_as_prototype(m, p->type), tb_default_arena());
+		p->symbol = cast(TB_Symbol *)p->func;
+	}
+
+	cgValue proc_value = cg_value(p->symbol, p->type);
+	cg_add_entity(m, entity, proc_value);
+	cg_add_member(m, p->name, proc_value);
+	cg_add_procedure_value(m, p);
+
+
+	return p;
+}
+
+gb_internal cgProcedure *cg_procedure_create_dummy(cgModule *m, String const &link_name, Type *type) {
+	auto *prev_found = string_map_get(&m->members, link_name);
+	GB_ASSERT_MSG(prev_found == nullptr, "failed to create dummy procedure for: %.*s", LIT(link_name));
+
+	cgProcedure *p = gb_alloc_item(permanent_allocator(), cgProcedure);
+
+	p->module = m;
+	p->name = link_name;
+
+	p->type           = type;
+	p->type_expr      = nullptr;
+	p->body           = nullptr;
+	p->tags           = 0;
+	p->inlining       = ProcInlining_none;
+	p->is_foreign     = false;
+	p->is_export      = false;
+	p->is_entry_point = false;
+
+	gbAllocator a = heap_allocator();
+	p->children.allocator      = a;
+	// p->defer_stmts.allocator   = a;
+	// p->blocks.allocator        = a;
+	// p->branch_blocks.allocator = a;
+	// p->context_stack.allocator = a;
+	// map_init(&p->tuple_fix_map, 0);
+
+
+
+	char const *link_name_c = alloc_cstring(temporary_allocator(), link_name);
+
+	TB_Linkage linkage = TB_LINKAGE_PRIVATE;
+
+	p->func = tb_function_create(m->mod, link_name_c, linkage, TB_COMDAT_NONE);
+	tb_function_set_prototype(p->func, cg_procedure_type_as_prototype(m, p->type), tb_default_arena());
+	p->symbol = cast(TB_Symbol *)p->func;
+
+	cgValue proc_value = cg_value(p->symbol, p->type);
+	cg_add_member(m, p->name, proc_value);
+	cg_add_procedure_value(m, p);
+
+	return p;
+}
+
+gb_internal void cg_procedure_begin(cgProcedure *p) {
+	if (p == nullptr || p->func == nullptr) {
+		return;
+	}
+	gb_printf_err("cg_procedure_begin %.*s\n", LIT(p->name));
+}
+
+gb_internal void cg_procedure_end(cgProcedure *p) {
+	if (p == nullptr || p->func == nullptr) {
+		return;
+	}
+	gb_printf_err("cg_procedure_end %.*s\n", LIT(p->name));
+	tb_inst_ret(p->func, 0, nullptr);
+	tb_module_compile_function(p->module->mod, p->func, TB_ISEL_FAST);
+}
+
+gb_internal void  cg_procedure_build_body(cgProcedure *p) {
+	if (p->body == nullptr) {
+		return;
+	}
+
+	// TODO(bill):
+}
+
+
+gb_internal bool cg_generate_code(Checker *c) {
+	TIME_SECTION("Tilde Module Initializtion");
+
+	CheckerInfo *info = &c->info;
+	gb_unused(info);
+
+	cgModule *m = cg_module_create(c);
+	defer (cg_module_destroy(m));
+
+	TIME_SECTION("Tilde Global Variables");
+
+	cg_create_global_variables(m);
+
+	// isize global_variable_max_count = 0;
+	// bool already_has_entry_point = false;
+
+	// for (Entity *e : info->entities) {
+	// 	String name = e->token.string;
+
+	// 	if (e->kind == Entity_Variable) {
+	// 		global_variable_max_count++;
+	// 	} else if (e->kind == Entity_Procedure) {
+	// 		if ((e->scope->flags&ScopeFlag_Init) && name == "main") {
+	// 			GB_ASSERT(e == info->entry_point);
+	// 		}
+	// 		if (build_context.command_kind == Command_test &&
+	// 		    (e->Procedure.is_export || e->Procedure.link_name.len > 0)) {
+	// 			String link_name = e->Procedure.link_name;
+	// 			if (e->pkg->kind == Package_Runtime) {
+	// 				if (link_name == "main"           ||
+	// 				    link_name == "DllMain"        ||
+	// 				    link_name == "WinMain"        ||
+	// 				    link_name == "wWinMain"       ||
+	// 				    link_name == "mainCRTStartup" ||
+	// 				    link_name == "_start") {
+	// 					already_has_entry_point = true;
+	// 				}
+	// 			}
+	// 		}
+	// 	}
+	// }
+	// auto global_variables = array_make<cgGlobalVariable>(permanent_allocator(), 0, global_variable_max_count);
+
+	if (true) {
+		Type *proc_type = alloc_type_proc(nullptr, nullptr, 0, nullptr, 0, false, ProcCC_Odin);
+		cgProcedure *p = cg_procedure_create_dummy(m, str_lit(CG_STARTUP_RUNTIME_PROC_NAME), proc_type);
+		p->is_startup = true;
+
+		cg_procedure_begin(p);
+		cg_procedure_end(p);
+	}
+
+	if (true) {
+		Type *proc_type = alloc_type_proc(nullptr, nullptr, 0, nullptr, 0, false, ProcCC_Odin);
+		cgProcedure *p = cg_procedure_create_dummy(m, str_lit(CG_CLEANUP_RUNTIME_PROC_NAME), proc_type);
+		p->is_startup = true;
+
+		cg_procedure_begin(p);
+		cg_procedure_end(p);
+	}
+
+	auto *min_dep_set = &info->minimum_dependency_set;
+
+	for (Entity *e : info->entities) {
+		String  name  = e->token.string;
+		Scope * scope = e->scope;
+
+		if ((scope->flags & ScopeFlag_File) == 0) {
+			continue;
+		}
+
+		Scope *package_scope = scope->parent;
+		GB_ASSERT(package_scope->flags & ScopeFlag_Pkg);
+
+		if (e->kind != Entity_Procedure) {
+			continue;
+		}
+
+		if (!ptr_set_exists(min_dep_set, e)) {
+			// NOTE(bill): Nothing depends upon it so doesn't need to be built
+			continue;
+		}
+		if (cgProcedure *p = cg_procedure_create(m, e)) {
+			array_add(&m->procedures_to_generate, p);
+		}
+	}
+
+
+	for (isize i = 0; i < m->procedures_to_generate.count; i++) {
+		cgProcedure *p = m->procedures_to_generate[i];
+		cg_procedure_begin(p);
+		if (p->name == "main") {
+			cg_procedure_build_body(p);
+		}
+		cg_procedure_end(p);
+	}
+
+
+
+	////////////////////////////////////////////////////////////////////////////////////
+
 
 
 	TB_Arena *arena = tb_default_arena();
 	TB_Arena *arena = tb_default_arena();
 
 
@@ -25,36 +718,36 @@ bool tb_generate_code(Checker *c) {
 	{
 	{
 		TB_Global *str_data = nullptr;
 		TB_Global *str_data = nullptr;
 		{
 		{
-			str_data = tb_global_create(m, "csb$1", nullptr, TB_LINKAGE_PRIVATE);
-			tb_global_set_storage(m, tb_module_get_rdata(m), str_data, 8, 1, 1);
-			void *region = tb_global_add_region(m, str_data, 0, 8);
+			str_data = tb_global_create(m->mod, "csb$1", nullptr, TB_LINKAGE_PRIVATE);
+			tb_global_set_storage(m->mod, tb_module_get_rdata(m->mod), str_data, 8, 1, 1);
+			void *region = tb_global_add_region(m->mod, str_data, 0, 8);
 			memcpy(region, "Hellope\x00", 8);
 			memcpy(region, "Hellope\x00", 8);
 		}
 		}
 
 
-		str = tb_global_create(m, "global$str", nullptr, TB_LINKAGE_PRIVATE);
-		tb_global_set_storage(m, tb_module_get_rdata(m), str, 16, 8, 2);
-		tb_global_add_symbol_reloc(m, str, 0, cast(TB_Symbol *)str_data);
-		void *len = tb_global_add_region(m, str, 8, 8);
+		str = tb_global_create(m->mod, "global$str", nullptr, TB_LINKAGE_PRIVATE);
+		tb_global_set_storage(m->mod, tb_module_get_rdata(m->mod), str, 16, 8, 2);
+		tb_global_add_symbol_reloc(m->mod, str, 0, cast(TB_Symbol *)str_data);
+		void *len = tb_global_add_region(m->mod, str, 8, 8);
 		*cast(i64 *)len = 7;
 		*cast(i64 *)len = 7;
 	}
 	}
 
 
 	{
 	{
 		TB_PrototypeParam printf_ret = {TB_TYPE_I32};
 		TB_PrototypeParam printf_ret = {TB_TYPE_I32};
 		TB_PrototypeParam printf_params = {TB_TYPE_PTR};
 		TB_PrototypeParam printf_params = {TB_TYPE_PTR};
-		TB_FunctionPrototype *printf_proto = tb_prototype_create(m, TB_STDCALL, 1, &printf_params, 1, &printf_ret, true);
-		TB_External *printf_proc = tb_extern_create(m, "printf", TB_EXTERNAL_SO_LOCAL);
+		TB_FunctionPrototype *printf_proto = tb_prototype_create(m->mod, TB_STDCALL, 1, &printf_params, 1, &printf_ret, true);
+		TB_External *printf_proc = tb_extern_create(m->mod, "printf", TB_EXTERNAL_SO_LOCAL);
 
 
 		TB_PrototypeParam main_ret = {TB_TYPE_I32};
 		TB_PrototypeParam main_ret = {TB_TYPE_I32};
-		TB_FunctionPrototype *main_proto = tb_prototype_create(m, TB_STDCALL, 0, nullptr, 1, &main_ret, false);
-		TB_Function *         p = tb_function_create(m, "main", TB_LINKAGE_PUBLIC, TB_COMDAT_NONE);
+		TB_FunctionPrototype *main_proto = tb_prototype_create(m->mod, TB_STDCALL, 0, nullptr, 1, &main_ret, false);
+		TB_Function *         p = tb_function_create(m->mod, "main", TB_LINKAGE_PUBLIC, TB_COMDAT_NONE);
 		tb_function_set_prototype(p, main_proto, arena);
 		tb_function_set_prototype(p, main_proto, arena);
 
 
 
 
-		auto str_ptr = tb_inst_get_symbol_address(p, cast(TB_Symbol *)str);
+		auto str_ptr          = tb_inst_get_symbol_address(p, cast(TB_Symbol *)str);
 		auto str_data_ptr_ptr = tb_inst_member_access(p, str_ptr, 0);
 		auto str_data_ptr_ptr = tb_inst_member_access(p, str_ptr, 0);
-		auto str_data_ptr = tb_inst_load(p, TB_TYPE_PTR, str_data_ptr_ptr, 1, false);
-		auto str_len_ptr = tb_inst_member_access(p, str_ptr, 8);
-		auto str_len = tb_inst_load(p, TB_TYPE_I64, str_len_ptr, 8, false);
+		auto str_data_ptr     = tb_inst_load(p, TB_TYPE_PTR, str_data_ptr_ptr, 1, false);
+		auto str_len_ptr      = tb_inst_member_access(p, str_ptr, 8);
+		auto str_len          = tb_inst_load(p, TB_TYPE_I64, str_len_ptr, 8, false);
 
 
 
 
 		TB_Node *params[4] = {};
 		TB_Node *params[4] = {};
@@ -64,17 +757,39 @@ bool tb_generate_code(Checker *c) {
 		params[3] = tb_inst_cstring(p, "World");
 		params[3] = tb_inst_cstring(p, "World");
 		TB_MultiOutput output = tb_inst_call(p, printf_proto, tb_inst_get_symbol_address(p, cast(TB_Symbol *)printf_proc), gb_count_of(params), params);
 		TB_MultiOutput output = tb_inst_call(p, printf_proto, tb_inst_get_symbol_address(p, cast(TB_Symbol *)printf_proc), gb_count_of(params), params);
 		gb_unused(output);
 		gb_unused(output);
+		TB_Node *printf_return_value = output.single;
+
+		TB_Node *zero = tb_inst_uint(p, TB_TYPE_I32, 0);
+		TB_Node *one  = tb_inst_uint(p, TB_TYPE_I32, 1);
+
+		TB_Node *prev_case = tb_inst_get_control(p);
+
+		TB_Node *true_case  = tb_inst_region(p);
+		TB_Node *false_case = tb_inst_region(p);
+
+		TB_Node *cond = tb_inst_cmp_igt(p, printf_return_value, zero, true);
+		tb_inst_if(p, cond, true_case, false_case);
 
 
-		TB_Node *value = tb_inst_uint(p, TB_TYPE_I32, 0);
-		tb_inst_ret(p, 1, &value);
+		tb_inst_set_control(p, true_case);
+		tb_inst_ret(p, 1, &zero);
 
 
-		tb_module_compile_function(m, p, TB_ISEL_FAST);
+		tb_inst_set_control(p, false_case);
+		tb_inst_ret(p, 1, &one);
 
 
-		TB_ExportBuffer export_buffer = tb_module_object_export(m, TB_DEBUGFMT_NONE);
+		tb_inst_set_control(p, prev_case);
+
+
+		tb_module_compile_function(m->mod, p, TB_ISEL_FAST);
+
+		tb_function_print(p, tb_default_print_callback, stdout);
+
+		TB_ExportBuffer export_buffer = tb_module_object_export(m->mod, TB_DEBUGFMT_NONE);
 		defer (tb_export_buffer_free(export_buffer));
 		defer (tb_export_buffer_free(export_buffer));
 
 
 		char const *path = "W:/Odin/tilde_main.obj";
 		char const *path = "W:/Odin/tilde_main.obj";
 		GB_ASSERT(tb_export_buffer_to_file(export_buffer, path));
 		GB_ASSERT(tb_export_buffer_to_file(export_buffer, path));
 	}
 	}
-	return false;
-}
+	return true;
+}
+
+#undef ABI_PKG_NAME_SEPARATOR

+ 160 - 0
src/tilde_backend.hpp

@@ -0,0 +1,160 @@
+#if defined(GB_SYSTEM_WINDOWS)
+	#pragma warning(push)
+	#pragma warning(disable: 4200)
+	#pragma warning(disable: 4201)
+	#define restrict gb_restrict
+#endif
+
+#include "tilde/tb.h"
+
+#if defined(GB_SYSTEM_WINDOWS)
+	#pragma warning(pop)
+#endif
+
+#define CG_STARTUP_RUNTIME_PROC_NAME   "__$startup_runtime"
+#define CG_CLEANUP_RUNTIME_PROC_NAME   "__$cleanup_runtime"
+#define CG_STARTUP_TYPE_INFO_PROC_NAME "__$startup_type_info"
+#define CG_TYPE_INFO_DATA_NAME       "__$type_info_data"
+#define CG_TYPE_INFO_TYPES_NAME      "__$type_info_types_data"
+#define CG_TYPE_INFO_NAMES_NAME      "__$type_info_names_data"
+#define CG_TYPE_INFO_OFFSETS_NAME    "__$type_info_offsets_data"
+#define CG_TYPE_INFO_USINGS_NAME     "__$type_info_usings_data"
+#define CG_TYPE_INFO_TAGS_NAME       "__$type_info_tags_data"
+
+struct cgModule;
+
+
+enum cgValueKind : u32 {
+	cgValue_Value,
+	cgValue_Addr,
+	cgValue_Symbol,
+};
+
+struct cgValue {
+	cgValueKind kind;
+	Type *      type;
+	union {
+		TB_Symbol *symbol;
+		TB_Node *  node;
+	};
+};
+
+enum cgAddrKind {
+	cgAddr_Default,
+	cgAddr_Map,
+	cgAddr_Context,
+	cgAddr_SoaVariable,
+
+	cgAddr_RelativePointer,
+	cgAddr_RelativeSlice,
+
+	cgAddr_Swizzle,
+	cgAddr_SwizzleLarge,
+};
+
+struct cgAddr {
+	cgAddrKind kind;
+	cgValue addr;
+	union {
+		struct {
+			cgValue key;
+			Type *type;
+			Type *result;
+		} map;
+		struct {
+			Selection sel;
+		} ctx;
+		struct {
+			cgValue index;
+			Ast *index_expr;
+		} soa;
+		struct {
+			cgValue index;
+			Ast *node;
+		} index_set;
+		struct {
+			bool deref;
+		} relative;
+		struct {
+			Type *type;
+			u8 count;      // 2, 3, or 4 components
+			u8 indices[4];
+		} swizzle;
+		struct {
+			Type *type;
+			Slice<i32> indices;
+		} swizzle_large;
+	};
+};
+
+
+struct cgProcedure {
+	u32 flags;
+	u16 state_flags;
+
+	cgProcedure *parent;
+	Array<cgProcedure *> children;
+
+	TB_Function *func;
+	TB_Symbol *symbol;
+
+	Entity *  entity;
+	cgModule *module;
+	String    name;
+	Type *    type;
+	Ast *     type_expr;
+	Ast *     body;
+	u64       tags;
+	ProcInlining inlining;
+	bool         is_foreign;
+	bool         is_export;
+	bool         is_entry_point;
+	bool         is_startup;
+
+	cgValue value;
+
+};
+
+
+struct cgModule {
+	TB_Module *  mod;
+	Checker *    checker;
+	CheckerInfo *info;
+
+	RwMutex values_mutex;
+	PtrMap<Entity *, cgValue> values;
+	StringMap<cgValue> members;
+
+	StringMap<cgProcedure *> procedures;
+	PtrMap<TB_Function *, Entity *> procedure_values;
+	Array<cgProcedure *> procedures_to_generate;
+
+	std::atomic<u32> nested_type_name_guid;
+};
+
+#ifndef ABI_PKG_NAME_SEPARATOR
+#define ABI_PKG_NAME_SEPARATOR "."
+#endif
+
+gb_global Entity *cg_global_type_info_data_entity   = {};
+gb_global cgAddr cg_global_type_info_member_types   = {};
+gb_global cgAddr cg_global_type_info_member_names   = {};
+gb_global cgAddr cg_global_type_info_member_offsets = {};
+gb_global cgAddr cg_global_type_info_member_usings  = {};
+gb_global cgAddr cg_global_type_info_member_tags    = {};
+
+gb_global isize cg_global_type_info_data_index           = 0;
+gb_global isize cg_global_type_info_member_types_index   = 0;
+gb_global isize cg_global_type_info_member_names_index   = 0;
+gb_global isize cg_global_type_info_member_offsets_index = 0;
+gb_global isize cg_global_type_info_member_usings_index  = 0;
+gb_global isize cg_global_type_info_member_tags_index    = 0;
+
+gb_internal cgValue cg_value(TB_Global *  g,    Type *type);
+gb_internal cgValue cg_value(TB_External *e,    Type *type);
+gb_internal cgValue cg_value(TB_Function *f,    Type *type);
+gb_internal cgValue cg_value(TB_Symbol *  s,    Type *type);
+gb_internal cgValue cg_value(TB_Node *    node, Type *type);
+
+gb_internal cgAddr cg_addr(cgValue const &value);
+