Quellcode durchsuchen

Begin work on the middle end system

gingerBill vor 3 Jahren
Ursprung
Commit
714a5e8931
6 geänderte Dateien mit 1025 neuen und 12 gelöschten Zeilen
  1. 1 1
      src/checker_builtin_procs.hpp
  2. 6 0
      src/entity.cpp
  3. 0 11
      src/llvm_backend.cpp
  4. 16 0
      src/main.cpp
  5. 632 0
      src/middle_end.cpp
  6. 370 0
      src/middle_end.hpp

+ 1 - 1
src/checker_builtin_procs.hpp

@@ -1,6 +1,6 @@
 // checker_builtin_procs.hpp
 // checker_builtin_procs.hpp
 
 
-enum BuiltinProcId {
+enum BuiltinProcId : u16 {
 	BuiltinProc_Invalid,
 	BuiltinProc_Invalid,
 
 
 	BuiltinProc_len,
 	BuiltinProc_len,

+ 6 - 0
src/entity.cpp

@@ -2,6 +2,9 @@ struct Scope;
 struct Checker;
 struct Checker;
 struct Type;
 struct Type;
 struct DeclInfo;
 struct DeclInfo;
+
+struct meProcedure;
+
 struct lbModule;
 struct lbModule;
 struct lbProcedure;
 struct lbProcedure;
 
 
@@ -173,6 +176,9 @@ struct Entity {
 	lbModule *   code_gen_module;
 	lbModule *   code_gen_module;
 	lbProcedure *code_gen_procedure;
 	lbProcedure *code_gen_procedure;
 
 
+	meProcedure *me_procedure;
+
+
 	u64         order_in_src;
 	u64         order_in_src;
 	String      deprecated_message;
 	String      deprecated_message;
 	String      warning_message;
 	String      warning_message;

+ 0 - 11
src/llvm_backend.cpp

@@ -1,14 +1,3 @@
-#define MULTITHREAD_OBJECT_GENERATION 1
-
-#ifndef USE_SEPARATE_MODULES
-#define USE_SEPARATE_MODULES build_context.use_separate_modules
-#endif
-
-#ifndef MULTITHREAD_OBJECT_GENERATION
-#define MULTITHREAD_OBJECT_GENERATION 0
-#endif
-
-
 #include "llvm_backend.hpp"
 #include "llvm_backend.hpp"
 #include "llvm_abi.cpp"
 #include "llvm_abi.cpp"
 #include "llvm_backend_opt.cpp"
 #include "llvm_backend_opt.cpp"

+ 16 - 0
src/main.cpp

@@ -46,6 +46,17 @@ gb_global Timings global_timings = {0};
 #include "checker.cpp"
 #include "checker.cpp"
 #include "docs.cpp"
 #include "docs.cpp"
 
 
+#define MULTITHREAD_OBJECT_GENERATION 1
+
+#ifndef USE_SEPARATE_MODULES
+#define USE_SEPARATE_MODULES build_context.use_separate_modules
+#endif
+
+#ifndef MULTITHREAD_OBJECT_GENERATION
+#define MULTITHREAD_OBJECT_GENERATION 0
+#endif
+
+#include "middle_end.cpp"
 
 
 #include "llvm_backend.cpp"
 #include "llvm_backend.cpp"
 
 
@@ -2732,6 +2743,11 @@ int main(int arg_count, char const **arg_ptr) {
 		return 0;
 		return 0;
 	}
 	}
 
 
+	MAIN_TIME_SECTION("Middle End Pass");
+	if (!me_generate(checker)) {
+		return 1;
+	}
+
 	MAIN_TIME_SECTION("LLVM API Code Gen");
 	MAIN_TIME_SECTION("LLVM API Code Gen");
 	lbGenerator *gen = gb_alloc_item(permanent_allocator(), lbGenerator);
 	lbGenerator *gen = gb_alloc_item(permanent_allocator(), lbGenerator);
 	if (!lb_init_generator(gen, checker)) {
 	if (!lb_init_generator(gen, checker)) {

+ 632 - 0
src/middle_end.cpp

@@ -0,0 +1,632 @@
+#include "middle_end.hpp"
+
+struct meGenerator {
+	CheckerInfo *info;
+
+	Array<String> output_object_paths;
+	Array<String> output_temp_paths;
+	String   output_base;
+	String   output_name;
+	PtrMap<AstPackage *, meModule *> modules;
+	meModule default_module;
+
+	PtrMap<Ast *, meProcedure *> anonymous_proc_lits;
+
+	std::atomic<u32> global_array_index;
+	std::atomic<u32> global_generated_index;
+};
+
+gb_internal meGenerator me_gen;
+
+gb_global Arena global_me_arena = {};
+gbAllocator me_allocator() {
+	return arena_allocator(&global_me_arena);
+}
+
+#define me_new(TYPE) gb_alloc_item(me_allocator(), TYPE)
+
+meValue me_value(meInstruction *instr) {
+	meValue value = {meValue_Instruction};
+	value.instr = instr;
+	return value;
+}
+meValue me_value(ExactValue *constant) {
+	meValue value = {meValue_ConstantValue};
+	value.constant = constant;
+	return value;
+}
+meValue me_value(meBlock *block) {
+	meValue value = {meValue_Block};
+	value.block = block;
+	return value;
+}
+meValue me_value(meProcedure *proc) {
+	meValue value = {meValue_Procedure};
+	value.proc = proc;
+	return value;
+}
+meValue me_value(meGlobalVariable *global) {
+	meValue value = {meValue_GlobalVariable};
+	value.global = global;
+	return value;
+}
+meValue me_value(meParameter *param) {
+	meValue value = {meValue_Parameter};
+	value.param = param;
+	return value;
+}
+
+meModule *me_pkg_module(AstPackage *pkg) {
+	if (pkg != nullptr) {
+		auto *found = map_get(&me_gen.modules, pkg);
+		if (found) {
+			return *found;
+		}
+	}
+	return &me_gen.default_module;
+}
+
+
+void me_add_entity(meModule *m, Entity *e, meValue val) {
+	if (e != nullptr) {
+		map_set(&m->values, e, val);
+	}
+}
+void me_add_member(meModule *m, String const &name, meValue val) {
+	if (name.len > 0) {
+		string_map_set(&m->members, name, val);
+	}
+}
+void me_add_member(meModule *m, StringHashKey const &key, meValue val) {
+	string_map_set(&m->members, key, val);
+}
+void me_add_procedure_value(meModule *m, meProcedure *p) {
+	if (p->entity != nullptr) {
+		map_set(&m->procedure_values, p, p->entity);
+	}
+	string_map_set(&m->procedures, p->name, p);
+}
+
+
+void me_add_foreign_library_path(meModule *m, Entity *e) {
+	if (e == nullptr) {
+		return;
+	}
+	GB_ASSERT(e->kind == Entity_LibraryName);
+	GB_ASSERT(e->flags & EntityFlag_Used);
+
+	for_array(i, e->LibraryName.paths) {
+		String library_path = e->LibraryName.paths[i];
+		if (library_path.len == 0) {
+			continue;
+		}
+
+		bool ok = true;
+		for_array(path_index, m->foreign_library_paths) {
+			String path = m->foreign_library_paths[path_index];
+	#if defined(GB_SYSTEM_WINDOWS)
+			if (str_eq_ignore_case(path, library_path)) {
+	#else
+			if (str_eq(path, library_path)) {
+	#endif
+				ok = false;
+				break;
+			}
+		}
+
+		if (ok) {
+			array_add(&m->foreign_library_paths, library_path);
+		}
+	}
+}
+
+
+String me_mangle_name(meModule *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") {
+		pkgn = str_lit("llvm$");
+	}
+
+	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.%.*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 me_set_nested_type_name_ir_mangled_name(Entity *e, meProcedure *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->me_procedure != nullptr) {
+			p = proc->me_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 = ++p->module->nested_type_name_guid;
+		name_len = gb_snprintf(name_text, name_len, "%.*s.%.*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 u32 guid = 0;
+		guid += 1;
+		name_len = gb_snprintf(name_text, name_len, "_internal.%.*s-%u", LIT(ts_name), guid);
+
+		String name = make_string(cast(u8 *)name_text, name_len-1);
+		e->TypeName.ir_mangled_name = name;
+		return name;
+	}
+}
+
+
+String me_get_entity_name(meModule *m, Entity *e, String default_name) {
+	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 me_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 = me_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;
+}
+
+
+
+void me_module_init(meModule *m, Checker *c) {
+	m->info = &c->info;
+
+	gbString module_name = gb_string_make(heap_allocator(), "odin_package");
+	if (m->pkg) {
+		module_name = gb_string_appendc(module_name, "-");
+		module_name = gb_string_append_length(module_name, m->pkg->name.text, m->pkg->name.len);
+	} else if (USE_SEPARATE_MODULES) {
+		module_name = gb_string_appendc(module_name, "-builtin");
+	}
+
+	gbAllocator a = heap_allocator();
+	map_init(&m->values, a);
+	map_init(&m->soa_values, a);
+	string_map_init(&m->members, a);
+	map_init(&m->procedure_values, a);
+	string_map_init(&m->procedures, a);
+	string_map_init(&m->const_strings, a);
+	map_init(&m->equal_procs, a);
+	map_init(&m->hasher_procs, a);
+	array_init(&m->procedures_to_generate, a, 0, 1024);
+	array_init(&m->foreign_library_paths,  a, 0, 1024);
+	array_init(&m->missing_procedures_to_check, a, 0, 16);
+
+	string_map_init(&m->objc_classes, a);
+	string_map_init(&m->objc_selectors, a);
+}
+
+
+bool me_generator_init(Checker *c) {
+	if (global_error_collector.count != 0) {
+		return false;
+	}
+
+	isize tc = c->parser->total_token_count;
+	if (tc < 2) {
+		return false;
+	}
+
+	meGenerator *gen = &me_gen;
+
+	String init_fullpath = c->parser->init_fullpath;
+
+	if (build_context.out_filepath.len == 0) {
+		gen->output_name = remove_directory_from_path(init_fullpath);
+		gen->output_name = remove_extension_from_path(gen->output_name);
+		gen->output_name = string_trim_whitespace(gen->output_name);
+		if (gen->output_name.len == 0) {
+			gen->output_name = c->info.init_scope->pkg->name;
+		}
+		gen->output_base = gen->output_name;
+	} else {
+		gen->output_name = build_context.out_filepath;
+		gen->output_name = string_trim_whitespace(gen->output_name);
+		if (gen->output_name.len == 0) {
+			gen->output_name = c->info.init_scope->pkg->name;
+		}
+		isize pos = string_extension_position(gen->output_name);
+		if (pos < 0) {
+			gen->output_base = gen->output_name;
+		} else {
+			gen->output_base = substring(gen->output_name, 0, pos);
+		}
+	}
+	gbAllocator ha = heap_allocator();
+	array_init(&gen->output_object_paths, ha);
+	array_init(&gen->output_temp_paths, ha);
+
+	gen->output_base = path_to_full_path(ha, gen->output_base);
+
+	gbString output_file_path = gb_string_make_length(ha, gen->output_base.text, gen->output_base.len);
+	output_file_path = gb_string_appendc(output_file_path, ".obj");
+	defer (gb_string_free(output_file_path));
+
+	gen->info = &c->info;
+
+	map_init(&gen->modules, permanent_allocator(), gen->info->packages.entries.count*2);
+	map_init(&gen->anonymous_proc_lits, heap_allocator(), 1024);
+
+	if (USE_SEPARATE_MODULES) {
+		for_array(i, gen->info->packages.entries) {
+			AstPackage *pkg = gen->info->packages.entries[i].value;
+
+			auto m = gb_alloc_item(permanent_allocator(), meModule);
+			m->pkg = pkg;
+			map_set(&gen->modules, pkg, m);
+			me_module_init(m, c);
+		}
+	}
+
+	map_set(&gen->modules, cast(AstPackage *)nullptr, &gen->default_module);
+	me_module_init(&gen->default_module, c);
+	return true;
+}
+
+meProcedure *me_procedure_create(meModule *m, Entity *entity, bool ignore_body) {
+	GB_ASSERT(entity != nullptr);
+	GB_ASSERT(entity->kind == Entity_Procedure);
+	if (!entity->Procedure.is_foreign) {
+		GB_ASSERT_MSG(entity->flags & EntityFlag_ProcBodyChecked, "%.*s :: %s", LIT(entity->token.string), type_to_string(entity->type));
+	}
+
+	String link_name = {};
+
+	if (ignore_body) {
+		meModule *other_module = me_pkg_module(entity->pkg);
+		link_name = me_get_entity_name(other_module, entity);
+	} else {
+		link_name = me_get_entity_name(m, entity);
+	}
+
+	{
+		StringHashKey key = string_hash_string(link_name);
+		meValue *found = string_map_get(&m->members, key);
+		if (found) {
+			me_add_entity(m, entity, *found);
+			return string_map_must_get(&m->procedures, key);
+		}
+	}
+
+	meProcedure *p = me_new(meProcedure);
+
+	p->module = m;
+	entity->me_procedure = p;
+	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;
+	switch (pl->inlining) {
+	case ProcInlining_none:
+		break;
+	case ProcInlining_inline:
+		p->flags |= meProcedureFlag_Inline;
+		break;
+	case ProcInlining_no_inline:
+		p->flags |= meProcedureFlag_NoInline;
+		break;
+	}
+	if (entity->Procedure.is_foreign) {
+		p->flags |= meProcedureFlag_Foreign;
+	}
+	if (entity->Procedure.is_export) {
+		p->flags |= meProcedureFlag_Export;
+	}
+	p->flags &= ~meProcedureFlag_EntryPoint;
+
+	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;
+
+	if (p->flags & meProcedureFlag_Foreign) {
+		me_add_foreign_library_path(p->module, entity->Procedure.foreign_library);
+	}
+
+	if (entity->flags & EntityFlag_Cold) {
+		p->flags |= meProcedureFlag_Cold;
+	}
+
+	meValue proc_value = me_value(p);
+	me_add_entity(m, entity,  proc_value);
+	me_add_member(m, p->name, proc_value);
+	me_add_procedure_value(m, p);
+
+	p->linkage = meLinkage_Strong; // default
+
+	if (p->flags & meProcedureFlag_Export) {
+		p->linkage = meLinkage_Export;
+	} else if (!(p->flags & meProcedureFlag_Foreign)) {
+		if (!USE_SEPARATE_MODULES) {
+			p->linkage = meLinkage_Internal;
+			// NOTE(bill): if a procedure is defined in package runtime and uses a custom link name,
+			// then it is very likely it is required by LLVM and thus cannot have internal linkage
+			if (entity->pkg != nullptr && entity->pkg->kind == Package_Runtime && p->body != nullptr) {
+				GB_ASSERT(entity->kind == Entity_Procedure);
+				String link_name = entity->Procedure.link_name;
+				if (entity->flags & EntityFlag_CustomLinkName &&
+				    link_name != "") {
+					if (string_starts_with(link_name, str_lit("__"))) {
+						p->linkage = meLinkage_Strong;
+					} else {
+						p->linkage = meLinkage_Internal;
+					}
+				}
+			}
+		}
+	}
+	if (entity->flags & EntityFlag_CustomLinkage_Internal) {
+		p->linkage = meLinkage_Internal;
+	} else if (entity->flags & EntityFlag_CustomLinkage_Strong) {
+		p->linkage = meLinkage_Strong;
+	} else if (entity->flags & EntityFlag_CustomLinkage_Weak) {
+		p->linkage = meLinkage_Weak;
+	} else if (entity->flags & EntityFlag_CustomLinkage_LinkOnce) {
+		p->linkage = meLinkage_LinkOnce;
+	}
+
+	if (ignore_body) {
+		p->body = nullptr;
+		p->linkage = meLinkage_Strong;
+	}
+
+	return p;
+}
+
+meBlock *me_block_create(meProcedure *p, char const *name) {
+	auto *b = me_new(meBlock);
+	b->scope = p->curr_scope;
+	b->scope_index = p->scope_index;
+
+	b->preds.allocator = heap_allocator();
+	b->succs.allocator = heap_allocator();
+
+	array_add(&p->blocks, b);
+
+	return b;
+}
+
+void me_block_start(meProcedure *p, meBlock *b) {
+	p->curr_block = b;
+}
+
+void me_build_stmt(meProcedure *p, Ast *stmt) {
+
+}
+
+meContextData *me_push_context_onto_stack_from_implicit_parameter(meProcedure *p) {
+	// TODO(bill): me_push_context_onto_stack_from_implicit_parameter
+	return nullptr;
+}
+
+
+void me_procedure_body_begin(meProcedure *p) {
+	DeclInfo *decl = decl_info_of_entity(p->entity);
+	if (decl != nullptr) {
+		for_array(i, decl->labels) {
+			BlockLabel bl = decl->labels[i];
+			meBranchBlocks bb = {bl.label, nullptr, nullptr};
+			array_add(&p->branch_blocks, bb);
+		}
+	}
+
+	p->decl_block  = me_block_create(p, "decls");
+	p->entry_block = me_block_create(p, "entry");
+	me_block_start(p, p->entry_block);
+
+	GB_ASSERT(p->type != nullptr);
+
+	{
+		// TODO(bill): parameter types
+	}
+	if (p->type->Proc.calling_convention == ProcCC_Odin) {
+		me_push_context_onto_stack_from_implicit_parameter(p);
+	}
+
+	me_block_start(p, p->entry_block);
+}
+
+void me_procedure_body_end(meProcedure *p) {
+	// TODO(bill): me_procedure_body_end
+}
+
+
+void me_generate_procedure(meModule *m, meProcedure *p) {
+	if (p->is_done) {
+		return;
+	}
+	if (p->body != nullptr) {
+		m->curr_procedure = p;
+		me_procedure_body_begin(p);
+		me_build_stmt(p, p->body);
+		me_procedure_body_end(p);
+		p->is_done = true;
+		m->curr_procedure = nullptr;
+	}
+
+	gb_printf_err("[procedure] %.*s\n", LIT(p->name));
+
+	// Add Flags
+	if (p->body != nullptr) {
+		if (p->name == "memcpy" || p->name == "memmove" ||
+		    p->name == "runtime.mem_copy" || p->name == "mem_copy_non_overlapping" ||
+		    string_starts_with(p->name, str_lit("llvm.memcpy")) ||
+		    string_starts_with(p->name, str_lit("llvm.memmove"))) {
+			p->flags |= meProcedureFlag_WithoutMemcpy;
+		}
+	}
+}
+
+
+
+bool me_generate(Checker *c) {
+	if (!me_generator_init(c)) {
+		return false;
+	}
+	CheckerInfo *info = &c->info;
+	auto *min_dep_set = &info->minimum_dependency_set;
+
+
+	for_array(i, info->entities) {
+		Entity *e = info->entities[i];
+		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);
+
+		switch (e->kind) {
+		case Entity_Variable:
+			// NOTE(bill): Handled above as it requires a specific load order
+			continue;
+		case Entity_ProcGroup:
+		case Entity_TypeName:
+			continue;
+
+		case Entity_Procedure:
+			break;
+		}
+
+		if (!ptr_set_exists(min_dep_set, e)) {
+			// NOTE(bill): Nothing depends upon it so doesn't need to be built
+			continue;
+		}
+
+		meModule *m = me_pkg_module(e->pkg);
+		String mangled_name = me_get_entity_name(m, e);
+
+		switch (e->kind) {
+		case Entity_Procedure:
+			array_add(&m->procedures_to_generate, me_procedure_create(m, e));
+			break;
+		}
+	}
+
+	for_array(j, me_gen.modules.entries) {
+		meModule *m = me_gen.modules.entries[j].value;
+		for_array(i, m->procedures_to_generate) {
+			meProcedure *p = m->procedures_to_generate[i];
+			me_generate_procedure(m, p);
+		}
+	}
+
+	gb_printf_err("[middle end pass done]\n");
+	gb_exit(0);
+	return true;
+}

+ 370 - 0
src/middle_end.hpp

@@ -0,0 +1,370 @@
+/*
+	Odin Middle End Notation
+
+	"me" prefix means middle end
+*/
+
+struct meModule; // Build/Translation Unit
+struct meValue;
+struct meProcedure;
+struct meBlock;
+struct meInstruction;
+struct meGlobalVariable;
+
+enum meOpKind : u8 {
+	meOp_Invalid = 0,
+
+	meOp_Unreachable,
+	meOp_Return,
+	meOp_Jump,
+	meOp_CondJump,
+	meOp_Switch,
+	meOp_Phi,
+
+	// Unary Operators
+	meOp_FNeg,
+
+	// Binary Arithmetic Operators
+	meOp_Add,
+	meOp_Sub,
+	meOp_Mul,
+	meOp_Div,
+	meOp_Rem,
+
+	// Binary Bitwise Operators
+	meOp_Shl,
+	meOp_LShr,
+	meOp_AShr,
+	meOp_And,
+	meOp_Or,
+	meOp_Xor,
+
+	// Memory Operators
+	meOp_Alloca,
+	meOp_Load,
+	meOp_Store,
+	meOp_GetElementPtr,
+
+	// Cast Operators
+	meOp_Cast,
+	meOp_Transmute,
+
+	// Binary Comparison Operators
+	meOp_Eq,
+	meOp_NotEq,
+	meOp_Lt,
+	meOp_LtEq,
+	meOp_Gt,
+	meOp_GtEq,
+	meOp_Min,
+	meOp_Max,
+
+	// Ternary Operators
+	meOp_Select,
+
+	// Other
+	meOp_Call,
+	meOp_BuiltinCall,
+
+	meOp_ExtractValue,
+	meOp_Swizzle,
+
+	// Atomics
+	meOp_Fence,
+	meOp_AtomicCmpXchg,
+};
+
+enum meInstructionFlags : u16 {
+	meInstructionFlag_Volatile      = 1<<0,
+	meInstructionFlag_AtomicRMW     = 1<<1,
+	meInstructionFlag_ForceInline   = 1<<2,
+	meInstructionFlag_ForceNoInline = 1<<3,
+};
+
+enum meAtomicOrderingKind : u8 {
+	meAtomicOrdering_NotAtomic,
+	meAtomicOrdering_Unordered,
+	meAtomicOrdering_Monotonic,
+	meAtomicOrdering_Acquire,
+	meAtomicOrdering_Release,
+	meAtomicOrdering_AcquireRelease,
+	meAtomicOrdering_SequentiallyConsistent,
+	meAtomicOrdering_COUNT,
+};
+
+enum meLinkageKind : u8 {
+	meLinkage_Strong,
+	meLinkage_Weak,
+	meLinkage_Internal,
+	meLinkage_LinkOnce,
+	meLinkage_Export,
+};
+
+
+enum {me_INSTRUCTION_MAX_ARG_COUNT = 4};
+
+struct meInstruction {
+	meOpKind             op;
+	meAtomicOrderingKind atomic_ordering;
+	u16                  flags;
+	u16                  alignment;
+	u16                  uses;
+
+	Type *         type;
+	meProcedure *parent;
+	TokenPos       pos;
+
+	meValue *operands[me_INSTRUCTION_MAX_ARG_COUNT];
+	isize operand_count;
+
+	Slice<meValue> *extra_operands; // non-null if used
+};
+
+struct meBlock {
+	meProcedure *          parent;
+	String                 name;
+	Array<meInstruction *> instructions;
+	Scope *                scope;
+	i32                    scope_index;
+	Array<meBlock *>       preds;
+	Array<meBlock *>       succs;
+};
+
+struct meBranchBlocks {
+	Ast *label;
+	meBlock *break_;
+	meBlock *continue_;
+};
+
+struct meTargetList {
+	meTargetList *prev;
+	bool is_block;
+	meBlock *break_;
+	meBlock *continue_;
+	meBlock *fallthrough_;
+};
+
+enum meGlobalVariableFlags : u16 {
+	meGlobalVariableFlag_ThreadLocal_default      = 1<<0,
+	meGlobalVariableFlag_ThreadLocal_localdynamic = 1<<1,
+	meGlobalVariableFlag_ThreadLocal_initialexec  = 1<<2,
+	meGlobalVariableFlag_ThreadLocal_localexec    = 1<<3,
+};
+
+
+struct meGlobalVariable {
+	meModule *    module;
+	Entity *      entity;
+	Type *        type;
+	String        name; // link name
+	meLinkageKind linkage;
+	u16           flags;
+	ExactValue    value;
+	i32           uses;
+	DeclInfo *    decl;
+};
+
+struct meParameter {
+	String         name;
+	Entity *       entity;
+	meProcedure *parent;
+	i32            uses;
+};
+
+
+enum meValueKind : u8 {
+	meValue_Invalid = 0,
+	meValue_Instruction,
+	meValue_ConstantValue,
+	meValue_Block,
+	meValue_Procedure,
+	meValue_GlobalVariable,
+	meValue_Parameter,
+};
+struct meValue {
+	meValueKind kind;
+	union {
+		meInstruction    *instr;
+		ExactValue       *constant;
+		meBlock          *block;
+		meProcedure      *proc;
+		meGlobalVariable *global;
+		meParameter      *param;
+	};
+};
+
+enum meAddrKind : u32 {
+	meAddr_Default = 0,
+	meAddr_Map,
+	meAddr_Context,
+	meAddr_SoaVariable,
+
+	meAddr_RelativePointer,
+	meAddr_RelativeSlice,
+
+	meAddr_Swizzle,
+	meAddr_SwizzleLarge,
+};
+
+struct meAddr {
+	meAddrKind kind;
+	meValue    addr;
+	union {
+		struct {
+			meValue key;
+			Type *type;
+			Type *result;
+		} map;
+		struct {
+			Selection sel;
+		} ctx;
+		struct {
+			meValue index;
+			Ast *index_expr;
+		} soa;
+		struct {
+			meValue 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 meContextData {
+	meAddr ctx;
+	i32 scope_index;
+	i32 uses;
+};
+
+enum meDeferKind {
+	meDefer_Node,
+	meDefer_Proc,
+};
+
+struct meDefer {
+	meDeferKind kind;
+	i32         scope_index;
+	i32         context_stack_count;
+	meBlock *   block;
+	union {
+		Ast *stmt;
+		struct {
+			meValue deferred;
+			Array<meValue> result_as_args;
+		} proc;
+	};
+};
+
+
+enum meProcedureFlags : u32 {
+	meProcedureFlag_Foreign    = 1<<1,
+	meProcedureFlag_Export     = 1<<2,
+	meProcedureFlag_EntryPoint = 1<<3,
+	meProcedureFlag_Startup    = 1<<4,
+
+	meProcedureFlag_Inline    = 1<<5,
+	meProcedureFlag_NoInline  = 1<<6,
+
+	meProcedureFlag_Cold          = 1<<7,
+	meProcedureFlag_Hot           = 1<<8,
+	meProcedureFlag_WithoutMemcpy = 1<<9,
+};
+
+struct meProcedure {
+	meModule *       module;
+	Entity *         entity;
+	Type *           type;
+	String           name; // link name
+	meLinkageKind    linkage;
+	u32              flags;
+	i32              uses;
+	BuiltinProcId    builtin_id;
+
+	u16  state_flags;
+	bool is_done;
+
+	meProcedure *        parent;
+	Array<meProcedure *> children;
+
+	Ast *type_expr;
+	Ast *body;
+	u64  tags;
+
+	meAddr           return_ptr;
+	Array<meDefer>   defer_stmts;
+
+	Array<meBlock *> blocks;
+	meBlock *        decl_block;
+	meBlock *        entry_block;
+	meBlock *        curr_block;
+
+	Array<meBranchBlocks> branch_blocks;
+
+	Scope *curr_scope;
+	i32    scope_index;
+
+	meTargetList *target_list;
+
+	Ast *curr_stmt;
+
+	Array<Scope *> scope_stack;
+
+	Array<meContextData> context_stack;
+};
+
+struct meModule { // Build/Translation Unit
+	CheckerInfo *info;
+	AstPackage *pkg; // associated
+
+	PtrMap<Entity *, meValue> values;
+	PtrMap<Entity *, meAddr>  soa_values;
+	StringMap<meValue>  members;
+	StringMap<meProcedure *> procedures;
+	PtrMap<meProcedure *, Entity *> procedure_values;
+	Array<meProcedure *> missing_procedures_to_check;
+
+	StringMap<meValue> const_strings;
+
+	PtrMap<Type *, meProcedure *> equal_procs;
+	PtrMap<Type *, meProcedure *> hasher_procs;
+
+	u32 nested_type_name_guid;
+
+	Array<meProcedure *> procedures_to_generate;
+	Array<String> foreign_library_paths;
+
+	meProcedure *curr_procedure;
+
+	StringMap<meAddr> objc_classes;
+	StringMap<meAddr> objc_selectors;
+};
+
+
+bool me_generate(Checker *c);
+
+String me_get_entity_name(meModule *m, Entity *e, String default_name = {});
+
+meProcedure *me_procedure_create(meModule *m, Entity *entity, bool ignore_body=false);
+
+
+
+
+meValue me_value(meInstruction *instr);
+meValue me_value(ExactValue *constant);
+meValue me_value(meBlock *block);
+meValue me_value(meProcedure *proc);
+meValue me_value(meGlobalVariable *global);
+meValue me_value(meParameter *param);