2
0
Эх сурвалжийг харах

Merge pull request #5725 from odin-lang/bill/parapoly-diagnostics

`-para-poly-diagnostics`
gingerBill 1 долоо хоног өмнө
parent
commit
17394d8b14

+ 2 - 0
src/build_settings.cpp

@@ -548,6 +548,8 @@ struct BuildContext {
 	bool   ignore_microsoft_magic;
 	bool   linker_map_file;
 
+	bool   para_poly_diagnostics;
+
 	bool   use_single_module;
 	bool   use_separate_modules;
 	bool   module_per_file;

+ 1 - 0
src/check_expr.cpp

@@ -587,6 +587,7 @@ gb_internal bool find_or_generate_polymorphic_procedure(CheckerContext *old_c, E
 	d->proc_lit = proc_lit;
 	d->proc_checked_state = ProcCheckedState_Unchecked;
 	d->defer_use_checked = false;
+	d->para_poly_original = old_decl->entity;
 
 	Entity *entity = alloc_entity_procedure(nullptr, token, final_proc_type, tags);
 	entity->state.store(EntityState_Resolved);

+ 2 - 0
src/checker.hpp

@@ -218,6 +218,8 @@ struct DeclInfo {
 	Ast *         proc_lit;      // Ast_ProcLit
 	Type *        gen_proc_type; // Precalculated
 
+	Entity *     para_poly_original;
+
 	bool          is_using;
 	bool          where_clauses_evaluated;
 	bool          foreign_require_results;

+ 4 - 0
src/llvm_backend.cpp

@@ -3556,6 +3556,10 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
 	TIME_SECTION("LLVM Correct Entity Linkage");
 	lb_correct_entity_linkage(gen);
 
+	if (build_context.para_poly_diagnostics) {
+		lb_do_para_poly_diagnostics(gen);
+	}
+
 	llvm_error = nullptr;
 	defer (LLVMDisposeMessage(llvm_error));
 

+ 187 - 0
src/llvm_backend_utility.cpp

@@ -2787,3 +2787,190 @@ gb_internal LLVMAtomicOrdering llvm_atomic_ordering_from_odin(Ast *expr) {
 	ExactValue value = type_and_value_of_expr(expr).value;
 	return llvm_atomic_ordering_from_odin(value);
 }
+
+
+
+struct lbParaPolyEntry {
+	Entity *entity;
+	String  canonical_name;
+	isize   count;
+	isize   total_code_size;
+};
+
+gb_internal isize lb_total_code_size(lbProcedure *p) {
+	isize instruction_count = 0;
+
+	LLVMBasicBlockRef first = LLVMGetFirstBasicBlock(p->value);
+
+	for (LLVMBasicBlockRef block = first; block != nullptr; block = LLVMGetNextBasicBlock(block)) {
+		for (LLVMValueRef instr = LLVMGetFirstInstruction(block); instr != nullptr; instr = LLVMGetNextInstruction(instr)) {
+			instruction_count += 1;
+		}
+	}
+	return instruction_count;
+
+}
+
+gb_internal void lb_do_para_poly_diagnostics(lbGenerator *gen) {
+	PtrMap<Entity * /* Parent */, lbParaPolyEntry> procs = {};
+	map_init(&procs);
+	defer (map_destroy(&procs));
+
+	for (auto &entry : gen->modules) {
+		lbModule *m = entry.value;
+		for (lbProcedure *p : m->generated_procedures) {
+			Entity *e = p->entity;
+			if (e == nullptr) {
+				continue;
+			}
+			if (p->builder == nullptr) {
+				continue;
+			}
+
+			DeclInfo *d = e->decl_info;
+			Entity *para_poly_parent = d->para_poly_original;
+			if (para_poly_parent == nullptr) {
+				continue;
+			}
+
+			lbParaPolyEntry *entry = map_get(&procs, para_poly_parent);
+			if (entry == nullptr) {
+				lbParaPolyEntry entry = {};
+				entry.entity = para_poly_parent;
+				entry.count = 0;
+
+
+				gbString w = string_canonical_entity_name(permanent_allocator(), entry.entity);
+				String name = make_string_c(w);
+
+				for (isize i = 0; i < name.len; i++) {
+					String s = substring(name, i, name.len);
+					if (string_starts_with(s, str_lit(":proc"))) {
+						name = substring(name, 0, i);
+						break;
+					}
+				}
+
+				entry.canonical_name = name;
+
+				map_set(&procs, para_poly_parent, entry);
+			}
+			entry = map_get(&procs, para_poly_parent);
+			GB_ASSERT(entry != nullptr);
+			entry->count += 1;
+			entry->total_code_size += lb_total_code_size(p);
+		}
+	}
+
+
+	auto entries = array_make<lbParaPolyEntry>(heap_allocator(), 0, procs.count);
+	defer (array_free(&entries));
+
+	for (auto &entry : procs) {
+		array_add(&entries, entry.value);
+	}
+
+	array_sort(entries, [](void const *a, void const *b) -> int {
+		lbParaPolyEntry *x = cast(lbParaPolyEntry *)a;
+		lbParaPolyEntry *y = cast(lbParaPolyEntry *)b;
+		if (x->total_code_size > y->total_code_size) {
+			return -1;
+		}
+		if (x->total_code_size < y->total_code_size) {
+			return +1;
+		}
+		return string_compare(x->canonical_name, y->canonical_name);
+	});
+
+
+	gb_printf("Parametric Polymorphic Procedure Diagnostics\n");
+	gb_printf("------------------------------------------------------------------------------------------\n");
+
+	gb_printf("Sorted by Total Instruction Count Descending (Top 100)\n\n");
+	gb_printf("Total Instruction Count | Instantiation Count | Average Instruction Count | Procedure Name\n");
+
+	isize max_count = 100;
+	for (auto &entry : entries) {
+		isize   code_size = entry.total_code_size;
+		isize   count     = entry.count;
+		String  name      = entry.canonical_name;
+
+		f64 average = cast(f64)code_size / cast(f64)gb_max(count, 1);
+
+		gb_printf("%23td | %19d | %25.2f | %.*s\n", code_size, count, average, LIT(name));
+		if (max_count-- <= 0) {
+			break;
+		}
+	}
+
+	gb_printf("------------------------------------------------------------------------------------------\n");
+
+	array_sort(entries, [](void const *a, void const *b) -> int {
+		lbParaPolyEntry *x = cast(lbParaPolyEntry *)a;
+		lbParaPolyEntry *y = cast(lbParaPolyEntry *)b;
+		if (x->count > y->count) {
+			return -1;
+		}
+		if (x->count < y->count) {
+			return +1;
+		}
+
+		return string_compare(x->canonical_name, y->canonical_name);
+	});
+
+	gb_printf("Sorted by Total Instantiation Count Descending (Top 100)\n\n");
+	gb_printf("Instantiation Count | Total Instruction Count | Average Instruction Count | Procedure Name\n");
+
+	max_count = 100;
+	for (auto &entry : entries) {
+		isize   code_size = entry.total_code_size;
+		isize   count     = entry.count;
+		String  name      = entry.canonical_name;
+
+
+		f64 average = cast(f64)code_size / cast(f64)gb_max(count, 1);
+
+		gb_printf("%19d | %23td | %25.2f | %.*s\n", count, code_size, average, LIT(name));
+		if (max_count-- <= 0) {
+			break;
+		}
+	}
+
+	gb_printf("------------------------------------------------------------------------------------------\n");
+
+
+	array_sort(entries, [](void const *a, void const *b) -> int {
+		lbParaPolyEntry *x = cast(lbParaPolyEntry *)a;
+		lbParaPolyEntry *y = cast(lbParaPolyEntry *)b;
+		if (x->count < y->count) {
+			return -1;
+		}
+		if (x->count > y->count) {
+			return +1;
+		}
+
+		if (x->total_code_size > y->total_code_size) {
+			return -1;
+		}
+		if (x->total_code_size < y->total_code_size) {
+			return +1;
+		}
+
+		return string_compare(x->canonical_name, y->canonical_name);
+	});
+
+	gb_printf("Single Instanced Parametric Polymorphic Procedures\n\n");
+	gb_printf("Instruction Count | Procedure Name\n");
+	for (auto &entry : entries) {
+		isize   code_size = entry.total_code_size;
+		isize   count     = entry.count;
+		String  name      = entry.canonical_name;
+		if (count != 1) {
+			break;
+		}
+
+		gb_printf("%17td | %.*s\n", code_size, LIT(name));
+	}
+
+	gb_printf("------------------------------------------------------------------------------------------\n");
+}

+ 7 - 0
src/main.cpp

@@ -394,6 +394,8 @@ enum BuildFlagKind {
 
 	BuildFlag_IntegerDivisionByZero,
 
+	BuildFlag_ParaPolyDiagnostics,
+
 	// internal use only
 	BuildFlag_InternalFastISel,
 	BuildFlag_InternalIgnoreLazy,
@@ -619,6 +621,7 @@ gb_internal bool parse_build_flags(Array<String> args) {
 
 	add_flag(&build_flags, BuildFlag_IntegerDivisionByZero,   str_lit("integer-division-by-zero"),  BuildFlagParam_String, Command__does_check);
 
+	add_flag(&build_flags, BuildFlag_ParaPolyDiagnostics,     str_lit("para-poly-diagnostics"),     BuildFlagParam_None,    Command__does_build);
 
 	add_flag(&build_flags, BuildFlag_InternalFastISel,        str_lit("internal-fast-isel"),        BuildFlagParam_None,    Command_all);
 	add_flag(&build_flags, BuildFlag_InternalIgnoreLazy,      str_lit("internal-ignore-lazy"),      BuildFlagParam_None,    Command_all);
@@ -1562,6 +1565,10 @@ gb_internal bool parse_build_flags(Array<String> args) {
 							}
 							break;
 
+						case BuildFlag_ParaPolyDiagnostics:
+							build_context.para_poly_diagnostics = true;
+							break;
+
 						case BuildFlag_InternalFastISel:
 							build_context.fast_isel = true;
 							break;