Browse Source

Add @(init) attribute for procedures, allowing for procedures to be called at startup
These procedures will be called after global variables have been initialized as normal

gingerBill 3 years ago
parent
commit
2bdae52fed
7 changed files with 145 additions and 13 deletions
  1. 3 0
      src/check_decl.cpp
  2. 86 2
      src/checker.cpp
  3. 10 8
      src/checker.hpp
  4. 33 0
      src/common.cpp
  5. 1 0
      src/entity.cpp
  6. 11 3
      src/llvm_backend.cpp
  7. 1 0
      src/parser.hpp

+ 3 - 0
src/check_decl.cpp

@@ -722,6 +722,9 @@ void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
 	if (ac.test) {
 	if (ac.test) {
 		e->flags |= EntityFlag_Test;
 		e->flags |= EntityFlag_Test;
 	}
 	}
+	if (ac.init) {
+		e->flags |= EntityFlag_Init;
+	}
 	if (ac.set_cold) {
 	if (ac.set_cold) {
 		e->flags |= EntityFlag_Cold;
 		e->flags |= EntityFlag_Cold;
 	}
 	}

+ 86 - 2
src/checker.cpp

@@ -862,6 +862,7 @@ void init_checker_info(CheckerInfo *i) {
 	string_map_init(&i->packages, a);
 	string_map_init(&i->packages, a);
 	array_init(&i->variable_init_order, a);
 	array_init(&i->variable_init_order, a);
 	array_init(&i->testing_procedures, a, 0, 0);
 	array_init(&i->testing_procedures, a, 0, 0);
+	array_init(&i->init_procedures, a, 0, 0);
 	array_init(&i->required_foreign_imports_through_force, a, 0, 0);
 	array_init(&i->required_foreign_imports_through_force, a, 0, 0);
 
 
 
 
@@ -1266,7 +1267,7 @@ void add_entity_flags_from_file(CheckerContext *c, Entity *e, Scope *scope) {
 		AstPackage *pkg = c->file->pkg;
 		AstPackage *pkg = c->file->pkg;
 		if (pkg->kind == Package_Init && e->kind == Entity_Procedure && e->token.string == "main") {
 		if (pkg->kind == Package_Init && e->kind == Entity_Procedure && e->token.string == "main") {
 			// Do nothing
 			// Do nothing
-		} else if (e->flags & EntityFlag_Test) {
+		} else if (e->flags & (EntityFlag_Test|EntityFlag_Init)) {
 			// Do nothing
 			// Do nothing
 		} else {
 		} else {
 			e->flags |= EntityFlag_Lazy;
 			e->flags |= EntityFlag_Lazy;
@@ -1340,7 +1341,7 @@ bool could_entity_be_lazy(Entity *e, DeclInfo *d) {
 		return false;
 		return false;
 	}
 	}
 
 
-	if (e->flags & EntityFlag_Test) {
+	if (e->flags & (EntityFlag_Test|EntityFlag_Init)) {
 		return false;
 		return false;
 	} else if (e->kind == Entity_Variable && e->Variable.is_export) {
 	} else if (e->kind == Entity_Variable && e->Variable.is_export) {
 		return false;
 		return false;
@@ -1374,6 +1375,8 @@ bool could_entity_be_lazy(Entity *e, DeclInfo *d) {
 					return false;
 					return false;
 				} else if (name == "export") {
 				} else if (name == "export") {
 					return false;
 					return false;
+				} else if (name == "init") {
+					return false;
 				}
 				}
 			}
 			}
 		}
 		}
@@ -2054,6 +2057,29 @@ void generate_minimum_dependency_set(Checker *c, Entity *start) {
 			if (e->Procedure.is_export) {
 			if (e->Procedure.is_export) {
 				add_dependency_to_set(c, e);
 				add_dependency_to_set(c, e);
 			}
 			}
+			if (e->flags & EntityFlag_Init) {
+				Type *t = base_type(e->type);
+				GB_ASSERT(t->kind == Type_Proc);
+				
+				bool is_init = true;
+				
+				if (t->Proc.param_count != 0 || t->Proc.result_count != 0) {
+					gbString str = type_to_string(t);
+					error(e->token, "@(init) procedures must have a signature type with no parameters nor results, got %s", str);
+					gb_string_free(str);
+					is_init = false;
+				}
+				
+				if ((e->scope->flags & (ScopeFlag_File|ScopeFlag_Pkg)) == 0) {
+					error(e->token, "@(init) procedures must be declared at the file scope");
+					is_init = false;
+				}
+				
+				if (is_init) {
+					add_dependency_to_set(c, e);
+					array_add(&c->info.init_procedures, e);
+				}					
+			}
 			break;
 			break;
 		}
 		}
 	}
 	}
@@ -2611,6 +2637,12 @@ DECL_ATTRIBUTE_PROC(proc_decl_attribute) {
 			return false;
 			return false;
 		}
 		}
 		return true;
 		return true;
+	} else if (name == "init") {
+		if (value != nullptr) {
+			error(value, "'%.*s' expects no parameter, or a string literal containing \"file\" or \"package\"", LIT(name));
+		}
+		ac->init = true;
+		return true;
 	} else if (name == "deferred") {
 	} else if (name == "deferred") {
 		if (value != nullptr) {
 		if (value != nullptr) {
 			Operand o = {};
 			Operand o = {};
@@ -3154,6 +3186,7 @@ void check_collect_value_decl(CheckerContext *c, Ast *decl) {
 
 
 	EntityVisiblityKind entity_visibility_kind = c->foreign_context.visibility_kind;
 	EntityVisiblityKind entity_visibility_kind = c->foreign_context.visibility_kind;
 	bool is_test = false;
 	bool is_test = false;
+	bool is_init = false;
 
 
 	for_array(i, vd->attributes) {
 	for_array(i, vd->attributes) {
 		Ast *attr = vd->attributes[i];
 		Ast *attr = vd->attributes[i];
@@ -3211,6 +3244,8 @@ void check_collect_value_decl(CheckerContext *c, Ast *decl) {
 				j -= 1;
 				j -= 1;
 			} else if (name == "test") {
 			} else if (name == "test") {
 				is_test = true;
 				is_test = true;
+			} else if (name == "init") {
+				is_init = true;
 			}
 			}
 		}
 		}
 	}
 	}
@@ -3337,6 +3372,9 @@ void check_collect_value_decl(CheckerContext *c, Ast *decl) {
 				if (is_test) {
 				if (is_test) {
 					e->flags |= EntityFlag_Test;
 					e->flags |= EntityFlag_Test;
 				}
 				}
+				if (is_init) {
+					e->flags |= EntityFlag_Init;
+				}
 			} else if (init->kind == Ast_ProcGroup) {
 			} else if (init->kind == Ast_ProcGroup) {
 				ast_node(pg, ProcGroup, init);
 				ast_node(pg, ProcGroup, init);
 				e = alloc_entity_proc_group(d->scope, token, nullptr);
 				e = alloc_entity_proc_group(d->scope, token, nullptr);
@@ -4342,6 +4380,7 @@ void check_import_entities(Checker *c) {
 	for (isize pkg_index = 0; pkg_index < package_order.count; pkg_index++) {
 	for (isize pkg_index = 0; pkg_index < package_order.count; pkg_index++) {
 		ImportGraphNode *node = package_order[pkg_index];
 		ImportGraphNode *node = package_order[pkg_index];
 		AstPackage *pkg = node->pkg;
 		AstPackage *pkg = node->pkg;
+		pkg->order = 1+pkg_index;
 
 
 		for_array(i, pkg->files) {
 		for_array(i, pkg->files) {
 			AstFile *f = pkg->files[i];
 			AstFile *f = pkg->files[i];
@@ -5060,6 +5099,48 @@ void check_merge_queues_into_arrays(Checker *c) {
 	check_add_definitions_from_queues(c);
 	check_add_definitions_from_queues(c);
 }
 }
 
 
+GB_COMPARE_PROC(init_procedures_cmp) {
+	int cmp = 0;
+	Entity *x = *(Entity **)a;
+	Entity *y = *(Entity **)b;
+	if (x == y) {
+		cmp = 0;
+		return cmp;
+	}
+	
+	if (x->pkg != y->pkg) {
+		isize order_x = x->pkg ? x->pkg->order : 0;
+		isize order_y = y->pkg ? y->pkg->order : 0;
+		cmp = isize_cmp(order_x, order_y);
+		if (cmp) {
+			return cmp;
+		}
+	}
+	if (x->file != y->file) {
+		String fullpath_x = x->file ? x->file->fullpath : (String{});
+		String fullpath_y = y->file ? y->file->fullpath : (String{});
+		String file_x = filename_from_path(fullpath_x);
+		String file_y = filename_from_path(fullpath_y);
+		
+		cmp = string_compare(file_x, file_y);
+		if (cmp) {
+			return cmp;
+		}
+	}
+
+	
+	cmp = u64_cmp(x->order_in_src, y->order_in_src);
+	if (cmp) {
+		return cmp;
+	}
+	return i32_cmp(x->token.pos.offset, y->token.pos.offset);
+}
+
+
+void check_sort_init_procedures(Checker *c) {
+	gb_sort_array(c->info.init_procedures.data, c->info.init_procedures.count, init_procedures_cmp);
+}
+
 
 
 void check_parsed_files(Checker *c) {
 void check_parsed_files(Checker *c) {
 #define TIME_SECTION(str) do { debugf("[Section] %s\n", str); if (build_context.show_more_timings) timings_start_section(&global_timings, str_lit(str)); } while (0)
 #define TIME_SECTION(str) do { debugf("[Section] %s\n", str); if (build_context.show_more_timings) timings_start_section(&global_timings, str_lit(str)); } while (0)
@@ -5227,6 +5308,9 @@ void check_parsed_files(Checker *c) {
 	TIME_SECTION("sanity checks");
 	TIME_SECTION("sanity checks");
 	GB_ASSERT(c->info.entity_queue.count.load(std::memory_order_relaxed) == 0);
 	GB_ASSERT(c->info.entity_queue.count.load(std::memory_order_relaxed) == 0);
 	GB_ASSERT(c->info.definition_queue.count.load(std::memory_order_relaxed) == 0);
 	GB_ASSERT(c->info.definition_queue.count.load(std::memory_order_relaxed) == 0);
+	
+	TIME_SECTION("sort init procedures");
+	check_sort_init_procedures(c);
 
 
 	TIME_SECTION("type check finish");
 	TIME_SECTION("type check finish");
 
 

+ 10 - 8
src/checker.hpp

@@ -99,14 +99,6 @@ struct DeferredProcedure {
 
 
 
 
 struct AttributeContext {
 struct AttributeContext {
-	bool    is_export;
-	bool    is_static;
-	bool    require_results;
-	bool    require_declaration;
-	bool    has_disabled_proc;
-	bool    disabled_proc;
-	bool    test;
-	bool    set_cold;
 	String  link_name;
 	String  link_name;
 	String  link_prefix;
 	String  link_prefix;
 	String  link_section;
 	String  link_section;
@@ -115,6 +107,15 @@ struct AttributeContext {
 	String  deprecated_message;
 	String  deprecated_message;
 	String  warning_message;
 	String  warning_message;
 	DeferredProcedure deferred_procedure;
 	DeferredProcedure deferred_procedure;
+	bool    is_export           : 1;
+	bool    is_static           : 1;
+	bool    require_results     : 1;
+	bool    require_declaration : 1;
+	bool    has_disabled_proc   : 1;
+	bool    disabled_proc       : 1;
+	bool    test                : 1;
+	bool    init                : 1;
+	bool    set_cold            : 1;
 	u32 optimization_mode; // ProcedureOptimizationMode
 	u32 optimization_mode; // ProcedureOptimizationMode
 };
 };
 
 
@@ -284,6 +285,7 @@ struct CheckerInfo {
 
 
 
 
 	Array<Entity *> testing_procedures;
 	Array<Entity *> testing_procedures;
+	Array<Entity *> init_procedures;
 
 
 	Array<Entity *> definitions;
 	Array<Entity *> definitions;
 	Array<Entity *> entities;
 	Array<Entity *> entities;

+ 33 - 0
src/common.cpp

@@ -47,6 +47,39 @@ void debugf(char const *fmt, ...);
 #include "range_cache.cpp"
 #include "range_cache.cpp"
 
 
 
 
+int isize_cmp(isize x, isize y) {
+	if (x < y) {
+		return -1;
+	} else if (x > y) {
+		return +1;
+	}
+	return 0;
+}
+int u64_cmp(u64 x, u64 y) {
+	if (x < y) {
+		return -1;
+	} else if (x > y) {
+		return +1;
+	}
+	return 0;
+}
+int i64_cmp(i64 x, i64 y) {
+	if (x < y) {
+		return -1;
+	} else if (x > y) {
+		return +1;
+	}
+	return 0;
+}
+int i32_cmp(i32 x, i32 y) {
+	if (x < y) {
+		return -1;
+	} else if (x > y) {
+		return +1;
+	}
+	return 0;
+}
+
 u32 fnv32a(void const *data, isize len) {
 u32 fnv32a(void const *data, isize len) {
 	u8 const *bytes = cast(u8 const *)data;
 	u8 const *bytes = cast(u8 const *)data;
 	u32 h = 0x811c9dc5;
 	u32 h = 0x811c9dc5;

+ 1 - 0
src/entity.cpp

@@ -73,6 +73,7 @@ enum EntityFlag : u64 {
 	EntityFlag_SwitchValue   = 1ull<<29,
 	EntityFlag_SwitchValue   = 1ull<<29,
 
 
 	EntityFlag_Test          = 1ull<<30,
 	EntityFlag_Test          = 1ull<<30,
+	EntityFlag_Init          = 1ull<<31,
 
 
 	EntityFlag_Overridden    = 1ull<<63,
 	EntityFlag_Overridden    = 1ull<<63,
 
 

+ 11 - 3
src/llvm_backend.cpp

@@ -653,7 +653,7 @@ lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProcedure *start
 	lb_populate_function_pass_manager(main_module, default_function_pass_manager, false, build_context.optimization_level);
 	lb_populate_function_pass_manager(main_module, default_function_pass_manager, false, build_context.optimization_level);
 	LLVMFinalizeFunctionPassManager(default_function_pass_manager);
 	LLVMFinalizeFunctionPassManager(default_function_pass_manager);
 
 
-	Type *proc_type = alloc_type_proc(nullptr, nullptr, 0, nullptr, 0, false, ProcCC_CDecl);
+	Type *proc_type = alloc_type_proc(nullptr, nullptr, 0, nullptr, 0, false, ProcCC_Odin);
 
 
 	lbProcedure *p = lb_create_dummy_procedure(main_module, str_lit(LB_STARTUP_RUNTIME_PROC_NAME), proc_type);
 	lbProcedure *p = lb_create_dummy_procedure(main_module, str_lit(LB_STARTUP_RUNTIME_PROC_NAME), proc_type);
 	p->is_startup = true;
 	p->is_startup = true;
@@ -731,6 +731,13 @@ lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProcedure *start
 			var->is_initialized = true;
 			var->is_initialized = true;
 		}
 		}
 	}
 	}
+	
+	CheckerInfo *info = main_module->gen->info;
+	
+	for (Entity *e : info->init_procedures) {
+		lbValue value = lb_find_procedure_value_from_entity(main_module, e);
+		lb_emit_call(p, value, {}, ProcInlining_none, false);
+	}
 
 
 
 
 	lb_end_procedure_body(p);
 	lb_end_procedure_body(p);
@@ -799,8 +806,9 @@ lbProcedure *lb_create_main_procedure(lbModule *m, lbProcedure *startup_runtime)
 		lbAddr args = lb_addr(lb_find_runtime_value(p->module, str_lit("args__")));
 		lbAddr args = lb_addr(lb_find_runtime_value(p->module, str_lit("args__")));
 		lb_fill_slice(p, args, argv, argc);
 		lb_fill_slice(p, args, argv, argc);
 	}
 	}
-
-	LLVMBuildCall2(p->builder, LLVMGetElementType(lb_type(m, startup_runtime->type)), startup_runtime->value, nullptr, 0, "");
+	
+	lbValue startup_runtime_value = {startup_runtime->value, startup_runtime->type};
+	lb_emit_call(p, startup_runtime_value, {}, ProcInlining_none, false);
 
 
 	if (build_context.command_kind == Command_test) {
 	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 *t_Internal_Test = find_type_in_pkg(m->info, str_lit("testing"), str_lit("Internal_Test"));

+ 1 - 0
src/parser.hpp

@@ -174,6 +174,7 @@ struct AstPackage {
 	Array<AstFile *>      files;
 	Array<AstFile *>      files;
 	Array<AstForeignFile> foreign_files;
 	Array<AstForeignFile> foreign_files;
 	bool                  is_single_file;
 	bool                  is_single_file;
+	isize                 order;
 
 
 	MPMCQueue<AstPackageExportedEntity> exported_entity_queue;
 	MPMCQueue<AstPackageExportedEntity> exported_entity_queue;