Browse Source

Refactor llvm backend code into separate procedures to make it simpler to profile

gingerBill 2 years ago
parent
commit
23d0c52bf4
2 changed files with 484 additions and 431 deletions
  1. 483 430
      src/llvm_backend.cpp
  2. 1 1
      src/llvm_backend.hpp

+ 483 - 430
src/llvm_backend.cpp

@@ -1159,222 +1159,73 @@ gb_internal lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProc
 	return p;
 }
 
+gb_internal void lb_create_global_procedures_and_types(lbGenerator *gen, CheckerInfo *info) {
+	auto *min_dep_set = &info->minimum_dependency_set;
 
-gb_internal lbProcedure *lb_create_main_procedure(lbModule *m, lbProcedure *startup_runtime) {
-	LLVMPassManagerRef default_function_pass_manager = LLVMCreateFunctionPassManagerForModule(m->mod);
-	lb_populate_function_pass_manager(m, default_function_pass_manager, false, build_context.optimization_level);
-	LLVMFinalizeFunctionPassManager(default_function_pass_manager);
-
-	Type *params  = alloc_type_tuple();
-	Type *results = alloc_type_tuple();
-
-	Type *t_ptr_cstring = alloc_type_pointer(t_cstring);
-	
-	bool call_cleanup = true;
-
-	bool has_args = false;
-	bool is_dll_main = false;
-	String name = str_lit("main");
-	if (build_context.metrics.os == TargetOs_windows && build_context.build_mode == BuildMode_DynamicLibrary) {
-		is_dll_main = true;
-		name = str_lit("DllMain");
-		slice_init(&params->Tuple.variables, permanent_allocator(), 3);
-		params->Tuple.variables[0] = alloc_entity_param(nullptr, make_token_ident("hinstDLL"),   t_rawptr, false, true);
-		params->Tuple.variables[1] = alloc_entity_param(nullptr, make_token_ident("fdwReason"),  t_u32,    false, true);
-		params->Tuple.variables[2] = alloc_entity_param(nullptr, make_token_ident("lpReserved"), t_rawptr, false, true);
-		call_cleanup = false;
-	} else if (build_context.metrics.os == TargetOs_windows && (build_context.metrics.arch == TargetArch_i386 || build_context.no_crt)) {
-		name = str_lit("mainCRTStartup");
-	} else if (is_arch_wasm()) {
-		name = str_lit("_start");
-		call_cleanup = false;
-	} else {
-		has_args = true;
-		slice_init(&params->Tuple.variables, permanent_allocator(), 2);
-		params->Tuple.variables[0] = alloc_entity_param(nullptr, make_token_ident("argc"), t_i32, false, true);
-		params->Tuple.variables[1] = alloc_entity_param(nullptr, make_token_ident("argv"), t_ptr_cstring, false, true);
-	}
-
-	slice_init(&results->Tuple.variables, permanent_allocator(), 1);
-	results->Tuple.variables[0] = alloc_entity_param(nullptr, blank_token, t_i32, false, true);
-
-	Type *proc_type = alloc_type_proc(nullptr,
-		params, params->Tuple.variables.count,
-		results, results->Tuple.variables.count, false, ProcCC_CDecl);
-
-
-	lbProcedure *p = lb_create_dummy_procedure(m, name, proc_type);
-	p->is_startup = true;
-
-	lb_begin_procedure_body(p);
-
-	if (has_args) { // initialize `runtime.args__`
-		lbValue argc = {LLVMGetParam(p->value, 0), t_i32};
-		lbValue argv = {LLVMGetParam(p->value, 1), t_ptr_cstring};
-		LLVMSetValueName2(argc.value, "argc", 4);
-		LLVMSetValueName2(argv.value, "argv", 4);
-		argc = lb_emit_conv(p, argc, t_int);
-		lbAddr args = lb_addr(lb_find_runtime_value(p->module, str_lit("args__")));
-		lb_fill_slice(p, args, argv, argc);
-	}
-	
-	lbValue startup_runtime_value = {startup_runtime->value, startup_runtime->type};
-	lb_emit_call(p, startup_runtime_value, {}, ProcInlining_none);
+	for (Entity *e : info->entities) {
+		String  name  = e->token.string;
+		Scope * scope = e->scope;
 
-	if (build_context.command_kind == Command_test) {
-		Type *t_Internal_Test = find_type_in_pkg(m->info, str_lit("testing"), str_lit("Internal_Test"));
-		Type *array_type = alloc_type_array(t_Internal_Test, m->info->testing_procedures.count);
-		Type *slice_type = alloc_type_slice(t_Internal_Test);
-		lbAddr all_tests_array_addr = lb_add_global_generated(p->module, array_type, {});
-		lbValue all_tests_array = lb_addr_get_ptr(p, all_tests_array_addr);
+		if ((scope->flags & ScopeFlag_File) == 0) {
+			continue;
+		}
 
-		LLVMValueRef indices[2] = {};
-		indices[0] = LLVMConstInt(lb_type(m, t_i32), 0, false);
+		Scope *package_scope = scope->parent;
+		GB_ASSERT(package_scope->flags & ScopeFlag_Pkg);
 
-		isize testing_proc_index = 0;
-		for (Entity *testing_proc : m->info->testing_procedures) {
-			String name = testing_proc->token.string;
+		switch (e->kind) {
+		case Entity_Variable:
+			// NOTE(bill): Handled above as it requires a specific load order
+			continue;
+		case Entity_ProcGroup:
+			continue;
 
-			String pkg_name = {};
-			if (testing_proc->pkg != nullptr) {
-				pkg_name = testing_proc->pkg->name;
+		case Entity_TypeName:
+		case Entity_Procedure:
+			break;
+		case Entity_Constant:
+			if (build_context.ODIN_DEBUG) {
+				add_debug_info_for_global_constant_from_entity(gen, e);
 			}
-			lbValue v_pkg  = lb_find_or_add_entity_string(m, pkg_name);
-			lbValue v_name = lb_find_or_add_entity_string(m, name);
-			lbValue v_proc = lb_find_procedure_value_from_entity(m, testing_proc);
-
-			indices[1] = LLVMConstInt(lb_type(m, t_int), testing_proc_index++, false);
-
-			LLVMValueRef vals[3] = {};
-			vals[0] = v_pkg.value;
-			vals[1] = v_name.value;
-			vals[2] = v_proc.value;
-			GB_ASSERT(LLVMIsConstant(vals[0]));
-			GB_ASSERT(LLVMIsConstant(vals[1]));
-			GB_ASSERT(LLVMIsConstant(vals[2]));
-
-			LLVMValueRef dst = LLVMConstInBoundsGEP2(llvm_addr_type(m, all_tests_array), all_tests_array.value, indices, gb_count_of(indices));
-			LLVMValueRef src = llvm_const_named_struct(m, t_Internal_Test, vals, gb_count_of(vals));
-
-			LLVMBuildStore(p->builder, src, dst);
+			break;
 		}
 
-		lbAddr all_tests_slice = lb_add_local_generated(p, slice_type, true);
-		lb_fill_slice(p, all_tests_slice,
-		              lb_array_elem(p, all_tests_array),
-		              lb_const_int(m, t_int, m->info->testing_procedures.count));
-
-
-		lbValue runner = lb_find_package_value(m, str_lit("testing"), str_lit("runner"));
-
-		auto args = array_make<lbValue>(heap_allocator(), 1);
-		args[0] = lb_addr_load(p, all_tests_slice);
-		lb_emit_call(p, runner, args);
-	} else {
-		if (m->info->entry_point != nullptr) {
-			lbValue entry_point = lb_find_procedure_value_from_entity(m, m->info->entry_point);
-			lb_emit_call(p, entry_point, {}, ProcInlining_no_inline);
+		bool polymorphic_struct = false;
+		if (e->type != nullptr && e->kind == Entity_TypeName) {
+			Type *bt = base_type(e->type);
+			if (bt->kind == Type_Struct) {
+				polymorphic_struct = is_type_polymorphic(bt);
+			}
 		}
-	}
-
-	
-	if (call_cleanup) {
-		lbValue cleanup_runtime_value = lb_find_runtime_value(m, str_lit("_cleanup_runtime"));
-		lb_emit_call(p, cleanup_runtime_value, {}, ProcInlining_none);
-	}
-	
-
-	if (is_dll_main) {
-		LLVMBuildRet(p->builder, LLVMConstInt(lb_type(m, t_i32), 1, false));
-	} else {
-		LLVMBuildRet(p->builder, LLVMConstInt(lb_type(m, t_i32), 0, false));
-	}
-
-	lb_end_procedure_body(p);
-	
-
-	LLVMSetLinkage(p->value, LLVMExternalLinkage);
-	if (is_arch_wasm()) {
-		lb_set_wasm_export_attributes(p->value, p->name);
-	}
-	
-
-	if (!m->debug_builder && LLVMVerifyFunction(p->value, LLVMReturnStatusAction)) {
-		gb_printf_err("LLVM CODE GEN FAILED FOR PROCEDURE: %s\n", "main");
-		LLVMDumpValue(p->value);
-		gb_printf_err("\n\n\n\n");
-		LLVMVerifyFunction(p->value, LLVMAbortProcessAction);
-	}
-
-	lb_run_function_pass_manager(default_function_pass_manager, p);
-	return p;
-}
-
-gb_internal String lb_filepath_ll_for_module(lbModule *m) {
-	String path = concatenate3_strings(permanent_allocator(),
-		build_context.build_paths[BuildPath_Output].basename,
-		STR_LIT("/"),
-		build_context.build_paths[BuildPath_Output].name
-	);
-
-	if (m->pkg) {
-		path = concatenate3_strings(permanent_allocator(), path, STR_LIT("-"), m->pkg->name);
-	} else if (USE_SEPARATE_MODULES) {
-		path = concatenate_strings(permanent_allocator(), path, STR_LIT("-builtin"));
-	}
-	path = concatenate_strings(permanent_allocator(), path, STR_LIT(".ll"));
-
-	return path;
-}
-gb_internal String lb_filepath_obj_for_module(lbModule *m) {
-	String path = concatenate3_strings(permanent_allocator(),
-		build_context.build_paths[BuildPath_Output].basename,
-		STR_LIT("/"),
-		build_context.build_paths[BuildPath_Output].name
-	);
 
-	if (m->pkg) {
-		path = concatenate3_strings(permanent_allocator(), path, STR_LIT("-"), m->pkg->name);
-	}
+		if (!polymorphic_struct && !ptr_set_exists(min_dep_set, e)) {
+			// NOTE(bill): Nothing depends upon it so doesn't need to be built
+			continue;
+		}
 
-	String ext = {};
+		lbModule *m = &gen->default_module;
+		if (USE_SEPARATE_MODULES) {
+			m = lb_pkg_module(gen, e->pkg);
+		}
 
-	if (build_context.build_mode == BuildMode_Assembly) {
-		ext = STR_LIT(".S");
-	} else {
-		if (is_arch_wasm()) {
-			ext = STR_LIT(".wasm.o");
-		} else {
-			switch (build_context.metrics.os) {
-			case TargetOs_windows:
-				ext = STR_LIT(".obj");
-				break;
-			default:
-			case TargetOs_darwin:
-			case TargetOs_linux:
-			case TargetOs_essence:
-				ext = STR_LIT(".o");
-				break;
+		String mangled_name = lb_get_entity_name(m, e);
 
-			case TargetOs_freestanding:
-				switch (build_context.metrics.abi) {
-				default:
-				case TargetABI_Default:
-				case TargetABI_SysV:
-					ext = STR_LIT(".o");
-					break;
-				case TargetABI_Win64:
-					ext = STR_LIT(".obj");
-					break;
-				}
-				break;
+		switch (e->kind) {
+		case Entity_TypeName:
+			lb_type(m, e->type);
+			break;
+		case Entity_Procedure:
+			{
+				lbProcedure *p = lb_create_procedure(m, e);
+				array_add(&m->procedures_to_generate, p);
 			}
+			break;
 		}
 	}
-
-	return concatenate_strings(permanent_allocator(), path, ext);
 }
 
+gb_internal void lb_generate_procedure(lbModule *m, lbProcedure *p);
+
 
 gb_internal bool lb_is_module_empty(lbModule *m) {
 	if (LLVMGetFirstFunction(m->mod) == nullptr &&
@@ -1487,42 +1338,428 @@ gb_internal void lb_llvm_function_pass_per_module(lbModule *m) {
 		}
 	}
 
-	for (auto const &entry : m->equal_procs) {
-		lbProcedure *p = entry.value;
-		lb_llvm_function_pass_per_function_internal(m, p);
-	}
-	for (auto const &entry : m->hasher_procs) {
-		lbProcedure *p = entry.value;
-		lb_llvm_function_pass_per_function_internal(m, p);
-	}
-	for (auto const &entry : m->map_get_procs) {
-		lbProcedure *p = entry.value;
-		lb_llvm_function_pass_per_function_internal(m, p);
+	for (auto const &entry : m->equal_procs) {
+		lbProcedure *p = entry.value;
+		lb_llvm_function_pass_per_function_internal(m, p);
+	}
+	for (auto const &entry : m->hasher_procs) {
+		lbProcedure *p = entry.value;
+		lb_llvm_function_pass_per_function_internal(m, p);
+	}
+	for (auto const &entry : m->map_get_procs) {
+		lbProcedure *p = entry.value;
+		lb_llvm_function_pass_per_function_internal(m, p);
+	}
+	for (auto const &entry : m->map_set_procs) {
+		lbProcedure *p = entry.value;
+		lb_llvm_function_pass_per_function_internal(m, p);
+	}
+}
+
+
+struct lbLLVMModulePassWorkerData {
+	lbModule *m;
+	LLVMTargetMachineRef target_machine;
+};
+
+gb_internal WORKER_TASK_PROC(lb_llvm_module_pass_worker_proc) {
+	auto wd = cast(lbLLVMModulePassWorkerData *)data;
+
+	lb_run_remove_unused_function_pass(wd->m);
+	lb_run_remove_unused_globals_pass(wd->m);
+
+	LLVMPassManagerRef module_pass_manager = LLVMCreatePassManager();
+	lb_populate_module_pass_manager(wd->target_machine, module_pass_manager, build_context.optimization_level);
+	LLVMRunPassManager(module_pass_manager, wd->m->mod);
+	return 0;
+}
+
+
+
+gb_internal WORKER_TASK_PROC(lb_generate_procedures_worker_proc) {
+	lbModule *m = cast(lbModule *)data;
+	for (isize i = 0; i < m->procedures_to_generate.count; i++) {
+		lbProcedure *p = m->procedures_to_generate[i];
+		lb_generate_procedure(p->module, p);
+	}
+	return 0;
+}
+
+gb_internal void lb_generate_procedures(lbGenerator *gen, bool do_threading) {
+	for (auto const &entry : gen->modules) {
+		lbModule *m = entry.value;
+		if (do_threading) {
+			thread_pool_add_task(lb_generate_procedures_worker_proc, m);
+		} else {
+			lb_generate_procedures_worker_proc(m);
+		}
+	}
+
+	thread_pool_wait();
+}
+
+gb_internal WORKER_TASK_PROC(lb_generate_missing_procedures_to_check_worker_proc) {
+	lbModule *m = cast(lbModule *)data;
+	for (isize i = 0; i < m->missing_procedures_to_check.count; i++) {
+		lbProcedure *p = m->missing_procedures_to_check[i];
+		debugf("Generate missing procedure: %.*s\n", LIT(p->name));
+		lb_generate_procedure(m, p);
+	}
+	return 0;
+}
+
+gb_internal void lb_generate_missing_procedures(lbGenerator *gen, bool do_threading) {
+	for (auto const &entry : gen->modules) {
+		lbModule *m = entry.value;
+		// NOTE(bill): procedures may be added during generation
+		if (do_threading) {
+			thread_pool_add_task(lb_generate_missing_procedures_to_check_worker_proc, m);
+		} else {
+			lb_generate_missing_procedures_to_check_worker_proc(m);
+		}
+	}
+
+	thread_pool_wait();
+}
+
+gb_internal void lb_debug_info_complete_types_and_finalize(lbGenerator *gen) {
+	for (auto const &entry : gen->modules) {
+		lbModule *m = entry.value;
+		if (m->debug_builder != nullptr) {
+			lb_debug_complete_types(m);
+			LLVMDIBuilderFinalize(m->debug_builder);
+		}
+	}
+}
+
+gb_internal void lb_llvm_function_passes(lbGenerator *gen) {
+	for (auto const &entry : gen->modules) {
+		lbModule *m = entry.value;
+		lb_llvm_function_pass_per_module(m);
+	}
+	thread_pool_wait();
+}
+
+
+gb_internal void lb_llvm_module_passes(lbGenerator *gen, bool do_threading) {
+	for (auto const &entry : gen->modules) {
+		lbModule *m = entry.value;
+		auto wd = gb_alloc_item(permanent_allocator(), lbLLVMModulePassWorkerData);
+		wd->m = m;
+		wd->target_machine = m->target_machine;
+
+		if (do_threading) {
+			thread_pool_add_task(lb_llvm_module_pass_worker_proc, wd);
+		} else {
+			lb_llvm_module_pass_worker_proc(wd);
+		}
+	}
+	thread_pool_wait();
+}
+
+
+gb_internal String lb_filepath_ll_for_module(lbModule *m) {
+	String path = concatenate3_strings(permanent_allocator(),
+		build_context.build_paths[BuildPath_Output].basename,
+		STR_LIT("/"),
+		build_context.build_paths[BuildPath_Output].name
+	);
+
+	if (m->pkg) {
+		path = concatenate3_strings(permanent_allocator(), path, STR_LIT("-"), m->pkg->name);
+	} else if (USE_SEPARATE_MODULES) {
+		path = concatenate_strings(permanent_allocator(), path, STR_LIT("-builtin"));
+	}
+	path = concatenate_strings(permanent_allocator(), path, STR_LIT(".ll"));
+
+	return path;
+}
+gb_internal String lb_filepath_obj_for_module(lbModule *m) {
+	String path = concatenate3_strings(permanent_allocator(),
+		build_context.build_paths[BuildPath_Output].basename,
+		STR_LIT("/"),
+		build_context.build_paths[BuildPath_Output].name
+	);
+
+	if (m->pkg) {
+		path = concatenate3_strings(permanent_allocator(), path, STR_LIT("-"), m->pkg->name);
+	}
+
+	String ext = {};
+
+	if (build_context.build_mode == BuildMode_Assembly) {
+		ext = STR_LIT(".S");
+	} else {
+		if (is_arch_wasm()) {
+			ext = STR_LIT(".wasm.o");
+		} else {
+			switch (build_context.metrics.os) {
+			case TargetOs_windows:
+				ext = STR_LIT(".obj");
+				break;
+			default:
+			case TargetOs_darwin:
+			case TargetOs_linux:
+			case TargetOs_essence:
+				ext = STR_LIT(".o");
+				break;
+
+			case TargetOs_freestanding:
+				switch (build_context.metrics.abi) {
+				default:
+				case TargetABI_Default:
+				case TargetABI_SysV:
+					ext = STR_LIT(".o");
+					break;
+				case TargetABI_Win64:
+					ext = STR_LIT(".obj");
+					break;
+				}
+				break;
+			}
+		}
+	}
+
+	return concatenate_strings(permanent_allocator(), path, ext);
+}
+
+gb_internal bool lb_llvm_module_verification(lbGenerator *gen, char **llvm_error_ptr) {
+	for (auto const &entry : gen->modules) {
+		lbModule *m = entry.value;
+		if (LLVMVerifyModule(m->mod, LLVMReturnStatusAction, llvm_error_ptr)) {
+			gb_printf_err("LLVM Error:\n%s\n", *llvm_error_ptr);
+			if (build_context.keep_temp_files) {
+				TIME_SECTION("LLVM Print Module to File");
+				String filepath_ll = lb_filepath_ll_for_module(m);
+				if (LLVMPrintModuleToFile(m->mod, cast(char const *)filepath_ll.text, llvm_error_ptr)) {
+					gb_printf_err("LLVM Error: %s\n", *llvm_error_ptr);
+					gb_exit(1);
+					return false;
+				}
+			}
+			gb_exit(1);
+			return false;
+		}
+	}
+	*llvm_error_ptr = nullptr;
+	return true;
+}
+
+gb_internal void lb_add_foreign_library_paths(lbGenerator *gen) {
+	for (auto const &entry : gen->modules) {
+		lbModule *m = entry.value;
+		for (Entity *e : m->info->required_foreign_imports_through_force) {
+			lb_add_foreign_library_path(m, e);
+		}
+
+		if (lb_is_module_empty(m)) {
+			continue;
+		}
+	}
+}
+
+gb_internal bool lb_llvm_object_generation(lbGenerator *gen, LLVMCodeGenFileType code_gen_file_type, bool do_threading) {
+	char *llvm_error = nullptr;
+	defer (LLVMDisposeMessage(llvm_error));
+
+	if (do_threading) {
+		for (auto const &entry : gen->modules) {
+			lbModule *m = entry.value;
+			if (lb_is_module_empty(m)) {
+				continue;
+			}
+
+			String filepath_ll = lb_filepath_ll_for_module(m);
+			String filepath_obj = lb_filepath_obj_for_module(m);
+			array_add(&gen->output_object_paths, filepath_obj);
+			array_add(&gen->output_temp_paths, filepath_ll);
+
+			auto *wd = gb_alloc_item(permanent_allocator(), lbLLVMEmitWorker);
+			wd->target_machine = m->target_machine;
+			wd->code_gen_file_type = code_gen_file_type;
+			wd->filepath_obj = filepath_obj;
+			wd->m = m;
+			thread_pool_add_task(lb_llvm_emit_worker_proc, wd);
+		}
+
+		thread_pool_wait(&global_thread_pool);
+	} else {
+		for (auto const &entry : gen->modules) {
+			lbModule *m = entry.value;
+			if (lb_is_module_empty(m)) {
+				continue;
+			}
+
+			String filepath_obj = lb_filepath_obj_for_module(m);
+			array_add(&gen->output_object_paths, filepath_obj);
+
+			String short_name = remove_directory_from_path(filepath_obj);
+			gbString section_name = gb_string_make(heap_allocator(), "LLVM Generate Object: ");
+			section_name = gb_string_append_length(section_name, short_name.text, short_name.len);
+
+			TIME_SECTION_WITH_LEN(section_name, gb_string_length(section_name));
+
+			if (LLVMTargetMachineEmitToFile(m->target_machine, m->mod, cast(char *)filepath_obj.text, code_gen_file_type, &llvm_error)) {
+				gb_printf_err("LLVM Error: %s\n", llvm_error);
+				gb_exit(1);
+				return false;
+			}
+		}
+	}
+	return true;
+}
+
+
+
+gb_internal lbProcedure *lb_create_main_procedure(lbModule *m, lbProcedure *startup_runtime) {
+	LLVMPassManagerRef default_function_pass_manager = LLVMCreateFunctionPassManagerForModule(m->mod);
+	lb_populate_function_pass_manager(m, default_function_pass_manager, false, build_context.optimization_level);
+	LLVMFinalizeFunctionPassManager(default_function_pass_manager);
+
+	Type *params  = alloc_type_tuple();
+	Type *results = alloc_type_tuple();
+
+	Type *t_ptr_cstring = alloc_type_pointer(t_cstring);
+
+	bool call_cleanup = true;
+
+	bool has_args = false;
+	bool is_dll_main = false;
+	String name = str_lit("main");
+	if (build_context.metrics.os == TargetOs_windows && build_context.build_mode == BuildMode_DynamicLibrary) {
+		is_dll_main = true;
+		name = str_lit("DllMain");
+		slice_init(&params->Tuple.variables, permanent_allocator(), 3);
+		params->Tuple.variables[0] = alloc_entity_param(nullptr, make_token_ident("hinstDLL"),   t_rawptr, false, true);
+		params->Tuple.variables[1] = alloc_entity_param(nullptr, make_token_ident("fdwReason"),  t_u32,    false, true);
+		params->Tuple.variables[2] = alloc_entity_param(nullptr, make_token_ident("lpReserved"), t_rawptr, false, true);
+		call_cleanup = false;
+	} else if (build_context.metrics.os == TargetOs_windows && (build_context.metrics.arch == TargetArch_i386 || build_context.no_crt)) {
+		name = str_lit("mainCRTStartup");
+	} else if (is_arch_wasm()) {
+		name = str_lit("_start");
+		call_cleanup = false;
+	} else {
+		has_args = true;
+		slice_init(&params->Tuple.variables, permanent_allocator(), 2);
+		params->Tuple.variables[0] = alloc_entity_param(nullptr, make_token_ident("argc"), t_i32, false, true);
+		params->Tuple.variables[1] = alloc_entity_param(nullptr, make_token_ident("argv"), t_ptr_cstring, false, true);
+	}
+
+	slice_init(&results->Tuple.variables, permanent_allocator(), 1);
+	results->Tuple.variables[0] = alloc_entity_param(nullptr, blank_token, t_i32, false, true);
+
+	Type *proc_type = alloc_type_proc(nullptr,
+		params, params->Tuple.variables.count,
+		results, results->Tuple.variables.count, false, ProcCC_CDecl);
+
+
+	lbProcedure *p = lb_create_dummy_procedure(m, name, proc_type);
+	p->is_startup = true;
+
+	lb_begin_procedure_body(p);
+
+	if (has_args) { // initialize `runtime.args__`
+		lbValue argc = {LLVMGetParam(p->value, 0), t_i32};
+		lbValue argv = {LLVMGetParam(p->value, 1), t_ptr_cstring};
+		LLVMSetValueName2(argc.value, "argc", 4);
+		LLVMSetValueName2(argv.value, "argv", 4);
+		argc = lb_emit_conv(p, argc, t_int);
+		lbAddr args = lb_addr(lb_find_runtime_value(p->module, str_lit("args__")));
+		lb_fill_slice(p, args, argv, argc);
+	}
+
+	lbValue startup_runtime_value = {startup_runtime->value, startup_runtime->type};
+	lb_emit_call(p, startup_runtime_value, {}, ProcInlining_none);
+
+	if (build_context.command_kind == Command_test) {
+		Type *t_Internal_Test = find_type_in_pkg(m->info, str_lit("testing"), str_lit("Internal_Test"));
+		Type *array_type = alloc_type_array(t_Internal_Test, m->info->testing_procedures.count);
+		Type *slice_type = alloc_type_slice(t_Internal_Test);
+		lbAddr all_tests_array_addr = lb_add_global_generated(p->module, array_type, {});
+		lbValue all_tests_array = lb_addr_get_ptr(p, all_tests_array_addr);
+
+		LLVMValueRef indices[2] = {};
+		indices[0] = LLVMConstInt(lb_type(m, t_i32), 0, false);
+
+		isize testing_proc_index = 0;
+		for (Entity *testing_proc : m->info->testing_procedures) {
+			String name = testing_proc->token.string;
+
+			String pkg_name = {};
+			if (testing_proc->pkg != nullptr) {
+				pkg_name = testing_proc->pkg->name;
+			}
+			lbValue v_pkg  = lb_find_or_add_entity_string(m, pkg_name);
+			lbValue v_name = lb_find_or_add_entity_string(m, name);
+			lbValue v_proc = lb_find_procedure_value_from_entity(m, testing_proc);
+
+			indices[1] = LLVMConstInt(lb_type(m, t_int), testing_proc_index++, false);
+
+			LLVMValueRef vals[3] = {};
+			vals[0] = v_pkg.value;
+			vals[1] = v_name.value;
+			vals[2] = v_proc.value;
+			GB_ASSERT(LLVMIsConstant(vals[0]));
+			GB_ASSERT(LLVMIsConstant(vals[1]));
+			GB_ASSERT(LLVMIsConstant(vals[2]));
+
+			LLVMValueRef dst = LLVMConstInBoundsGEP2(llvm_addr_type(m, all_tests_array), all_tests_array.value, indices, gb_count_of(indices));
+			LLVMValueRef src = llvm_const_named_struct(m, t_Internal_Test, vals, gb_count_of(vals));
+
+			LLVMBuildStore(p->builder, src, dst);
+		}
+
+		lbAddr all_tests_slice = lb_add_local_generated(p, slice_type, true);
+		lb_fill_slice(p, all_tests_slice,
+		              lb_array_elem(p, all_tests_array),
+		              lb_const_int(m, t_int, m->info->testing_procedures.count));
+
+
+		lbValue runner = lb_find_package_value(m, str_lit("testing"), str_lit("runner"));
+
+		auto args = array_make<lbValue>(heap_allocator(), 1);
+		args[0] = lb_addr_load(p, all_tests_slice);
+		lb_emit_call(p, runner, args);
+	} else {
+		if (m->info->entry_point != nullptr) {
+			lbValue entry_point = lb_find_procedure_value_from_entity(m, m->info->entry_point);
+			lb_emit_call(p, entry_point, {}, ProcInlining_no_inline);
+		}
+	}
+
+
+	if (call_cleanup) {
+		lbValue cleanup_runtime_value = lb_find_runtime_value(m, str_lit("_cleanup_runtime"));
+		lb_emit_call(p, cleanup_runtime_value, {}, ProcInlining_none);
 	}
-	for (auto const &entry : m->map_set_procs) {
-		lbProcedure *p = entry.value;
-		lb_llvm_function_pass_per_function_internal(m, p);
+
+
+	if (is_dll_main) {
+		LLVMBuildRet(p->builder, LLVMConstInt(lb_type(m, t_i32), 1, false));
+	} else {
+		LLVMBuildRet(p->builder, LLVMConstInt(lb_type(m, t_i32), 0, false));
 	}
-}
 
+	lb_end_procedure_body(p);
 
-struct lbLLVMModulePassWorkerData {
-	lbModule *m;
-	LLVMTargetMachineRef target_machine;
-};
 
-gb_internal WORKER_TASK_PROC(lb_llvm_module_pass_worker_proc) {
-	auto wd = cast(lbLLVMModulePassWorkerData *)data;
+	LLVMSetLinkage(p->value, LLVMExternalLinkage);
+	if (is_arch_wasm()) {
+		lb_set_wasm_export_attributes(p->value, p->name);
+	}
 
-	lb_run_remove_unused_function_pass(wd->m);
-	lb_run_remove_unused_globals_pass(wd->m);
 
-	LLVMPassManagerRef module_pass_manager = LLVMCreatePassManager();
-	lb_populate_module_pass_manager(wd->target_machine, module_pass_manager, build_context.optimization_level);
-	LLVMRunPassManager(module_pass_manager, wd->m->mod);
-	return 0;
-}
+	if (!m->debug_builder && LLVMVerifyFunction(p->value, LLVMReturnStatusAction)) {
+		gb_printf_err("LLVM CODE GEN FAILED FOR PROCEDURE: %s\n", "main");
+		LLVMDumpValue(p->value);
+		gb_printf_err("\n\n\n\n");
+		LLVMVerifyFunction(p->value, LLVMAbortProcessAction);
+	}
 
+	lb_run_function_pass_manager(default_function_pass_manager, p);
+	return p;
+}
 
 gb_internal void lb_generate_procedure(lbModule *m, lbProcedure *p) {
 	if (p->is_done) {
@@ -1564,25 +1801,6 @@ gb_internal void lb_generate_procedure(lbModule *m, lbProcedure *p) {
 }
 
 
-gb_internal WORKER_TASK_PROC(lb_generate_procedures_worker_proc) {
-	lbModule *m = cast(lbModule *)data;
-	for (isize i = 0; i < m->procedures_to_generate.count; i++) {
-		lbProcedure *p = m->procedures_to_generate[i];
-		lb_generate_procedure(p->module, p);
-	}
-	return 0;
-}
-
-gb_internal WORKER_TASK_PROC(lb_generate_missing_procedures_to_check_worker_proc) {
-	lbModule *m = cast(lbModule *)data;
-	for (isize i = 0; i < m->missing_procedures_to_check.count; i++) {
-		lbProcedure *p = m->missing_procedures_to_check[i];
-		debugf("Generate missing procedure: %.*s\n", LIT(p->name));
-		lb_generate_procedure(m, p);
-	}
-	return 0;
-}
-
 gb_internal bool lb_generate_code(lbGenerator *gen) {
 	TIME_SECTION("LLVM Initializtion");
 
@@ -2064,82 +2282,14 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
 	}
 
 	TIME_SECTION("LLVM Global Procedures and Types");
-	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);
-
-		switch (e->kind) {
-		case Entity_Variable:
-			// NOTE(bill): Handled above as it requires a specific load order
-			continue;
-		case Entity_ProcGroup:
-			continue;
-
-		case Entity_TypeName:
-		case Entity_Procedure:
-			break;
-		case Entity_Constant:
-			if (build_context.ODIN_DEBUG) {
-				add_debug_info_for_global_constant_from_entity(gen, e);
-			}
-			break;
-		}
-
-		bool polymorphic_struct = false;
-		if (e->type != nullptr && e->kind == Entity_TypeName) {
-			Type *bt = base_type(e->type);
-			if (bt->kind == Type_Struct) {
-				polymorphic_struct = is_type_polymorphic(bt);
-			}
-		}
-
-		if (!polymorphic_struct && !ptr_set_exists(min_dep_set, e)) {
-			// NOTE(bill): Nothing depends upon it so doesn't need to be built
-			continue;
-		}
-
-		lbModule *m = &gen->default_module;
-		if (USE_SEPARATE_MODULES) {
-			m = lb_pkg_module(gen, e->pkg);
-		}
-
-		String mangled_name = lb_get_entity_name(m, e);
-
-		switch (e->kind) {
-		case Entity_TypeName:
-			lb_type(m, e->type);
-			break;
-		case Entity_Procedure:
-			{
-				lbProcedure *p = lb_create_procedure(m, e);
-				array_add(&m->procedures_to_generate, p);
-			}
-			break;
-		}
-	}
+	lb_create_global_procedures_and_types(gen, info);
 
 	if (gen->modules.entries.count <= 1) {
 		do_threading = false;
 	}
 
 	TIME_SECTION("LLVM Procedure Generation");
-	for (auto const &entry : gen->modules) {
-		lbModule *m = entry.value;
-		if (do_threading) {
-			thread_pool_add_task(lb_generate_procedures_worker_proc, m);
-		} else {
-			lb_generate_procedures_worker_proc(m);
-		}
-	}
-
-	thread_pool_wait();
+	lb_generate_procedures(gen, do_threading);
 
 	if (build_context.command_kind == Command_test && !already_has_entry_point) {
 		TIME_SECTION("LLVM main");
@@ -2147,17 +2297,7 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
 	}
 
 	TIME_SECTION("LLVM Procedure Generation (missing)");
-	for (auto const &entry : gen->modules) {
-		lbModule *m = entry.value;
-		// NOTE(bill): procedures may be added during generation
-		if (do_threading) {
-			thread_pool_add_task(lb_generate_missing_procedures_to_check_worker_proc, m);
-		} else {
-			lb_generate_missing_procedures_to_check_worker_proc(m);
-		}
-	}
-
-	thread_pool_wait();
+	lb_generate_missing_procedures(gen, do_threading);
 
 	if (gen->objc_names) {
 		TIME_SECTION("Finalize objc names");
@@ -2166,48 +2306,28 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
 
 	if (build_context.ODIN_DEBUG) {
 		TIME_SECTION("LLVM Debug Info Complete Types and Finalize");
+		lb_debug_info_complete_types_and_finalize(gen);
+	}
+
+	if (do_threading) {
+		isize non_empty_module_count = 0;
 		for (auto const &entry : gen->modules) {
 			lbModule *m = entry.value;
-			if (m->debug_builder != nullptr) {
-				lb_debug_complete_types(m);
-				LLVMDIBuilderFinalize(m->debug_builder);
+			if (!lb_is_module_empty(m)) {
+				non_empty_module_count += 1;
 			}
 		}
-	}
-
-	isize non_empty_module_count = 0;
-	for (auto const &entry : gen->modules) {
-		lbModule *m = entry.value;
-		if (!lb_is_module_empty(m)) {
-			non_empty_module_count += 1;
+		if (non_empty_module_count <= 1) {
+			do_threading = false;
 		}
 	}
-	if (non_empty_module_count <= 1) {
-		do_threading = false;
-	}
 
 
 	TIME_SECTION("LLVM Function Pass");
-	for (auto const &entry : gen->modules) {
-		lbModule *m = entry.value;
-		lb_llvm_function_pass_per_module(m);
-	}
-	thread_pool_wait();
+	lb_llvm_function_passes(gen);
 
 	TIME_SECTION("LLVM Module Pass");
-	for (auto const &entry : gen->modules) {
-		lbModule *m = entry.value;
-		auto wd = gb_alloc_item(permanent_allocator(), lbLLVMModulePassWorkerData);
-		wd->m = m;
-		wd->target_machine = m->target_machine;
-
-		if (do_threading) {
-			thread_pool_add_task(lb_llvm_module_pass_worker_proc, wd);
-		} else {
-			lb_llvm_module_pass_worker_proc(wd);
-		}
-	}
-	thread_pool_wait();
+	lb_llvm_module_passes(gen, do_threading);
 
 
 	TIME_SECTION("LLVM Module Verification");
@@ -2220,25 +2340,10 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
 		code_gen_file_type = LLVMAssemblyFile;
 	}
 
-
-	for (auto const &entry : gen->modules) {
-		lbModule *m = entry.value;
-		if (LLVMVerifyModule(m->mod, LLVMReturnStatusAction, &llvm_error)) {
-			gb_printf_err("LLVM Error:\n%s\n", llvm_error);
-			if (build_context.keep_temp_files) {
-				TIME_SECTION("LLVM Print Module to File");
-				String filepath_ll = lb_filepath_ll_for_module(m);
-				if (LLVMPrintModuleToFile(m->mod, cast(char const *)filepath_ll.text, &llvm_error)) {
-					gb_printf_err("LLVM Error: %s\n", llvm_error);
-					gb_exit(1);
-					return false;
-				}
-			}
-			gb_exit(1);
-			return false;
-		}
+	if (!lb_llvm_module_verification(gen, &llvm_error)) {
+		return false;
 	}
-	llvm_error = nullptr;
+
 	if (build_context.keep_temp_files ||
 	    build_context.build_mode == BuildMode_LLVM_IR) {
 		TIME_SECTION("LLVM Print Module to File");
@@ -2264,17 +2369,7 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
 	}
 
 	TIME_SECTION("LLVM Add Foreign Library Paths");
-
-	for (auto const &entry : gen->modules) {
-		lbModule *m = entry.value;
-		for (Entity *e : m->info->required_foreign_imports_through_force) {
-			lb_add_foreign_library_path(m, e);
-		}
-
-		if (lb_is_module_empty(m)) {
-			continue;
-		}
-	}
+	lb_add_foreign_library_paths(gen);
 
 	TIME_SECTION("LLVM Object Generation");
 	
@@ -2282,50 +2377,8 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
 		gb_printf_err("LLVM object generation has been ignored!\n");
 		return false;
 	}
-
-	if (do_threading) {
-		for (auto const &entry : gen->modules) {
-			lbModule *m = entry.value;
-			if (lb_is_module_empty(m)) {
-				continue;
-			}
-
-			String filepath_ll = lb_filepath_ll_for_module(m);
-			String filepath_obj = lb_filepath_obj_for_module(m);
-			array_add(&gen->output_object_paths, filepath_obj);
-			array_add(&gen->output_temp_paths, filepath_ll);
-
-			auto *wd = gb_alloc_item(permanent_allocator(), lbLLVMEmitWorker);
-			wd->target_machine = m->target_machine;
-			wd->code_gen_file_type = code_gen_file_type;
-			wd->filepath_obj = filepath_obj;
-			wd->m = m;
-			thread_pool_add_task(lb_llvm_emit_worker_proc, wd);
-		}
-
-		thread_pool_wait(&global_thread_pool);
-	} else {
-		for (auto const &entry : gen->modules) {
-			lbModule *m = entry.value;
-			if (lb_is_module_empty(m)) {
-				continue;
-			}
-
-			String filepath_obj = lb_filepath_obj_for_module(m);
-			array_add(&gen->output_object_paths, filepath_obj);
-
-			String short_name = remove_directory_from_path(filepath_obj);
-			gbString section_name = gb_string_make(heap_allocator(), "LLVM Generate Object: ");
-			section_name = gb_string_append_length(section_name, short_name.text, short_name.len);
-
-			TIME_SECTION_WITH_LEN(section_name, gb_string_length(section_name));
-
-			if (LLVMTargetMachineEmitToFile(m->target_machine, m->mod, cast(char *)filepath_obj.text, code_gen_file_type, &llvm_error)) {
-				gb_printf_err("LLVM Error: %s\n", llvm_error);
-				gb_exit(1);
-				return false;
-			}
-		}
+	if (!lb_llvm_object_generation(gen, code_gen_file_type, do_threading)) {
+		return false;
 	}
 
 	gb_sort_array(gen->foreign_libraries.data, gen->foreign_libraries.count, foreign_library_cmp);

+ 1 - 1
src/llvm_backend.hpp

@@ -37,7 +37,7 @@
 #endif
 
 #if LLVM_VERSION_MAJOR > 12 || (LLVM_VERSION_MAJOR == 12 && LLVM_VERSION_MINOR >= 0 && LLVM_VERSION_PATCH > 0)
-#define ODIN_LLVM_MINIMUM_VERSION_12 1
+#define ODIN_LLVM_MINIMUM_VERSION_12 0
 #else
 #define ODIN_LLVM_MINIMUM_VERSION_12 0
 #endif