From a7309b217e4d3e49caaf4ae9d84b51dea47bcb33 Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Mon, 16 Feb 2026 00:13:19 +0100 Subject: [PATCH] Add `--print-large-functions` for checking which functions likely dominate the compile time. --- releasenotes.md | 1 + src/build/build.h | 2 ++ src/build/build_options.c | 6 ++++++ src/build/builder.c | 2 ++ src/compiler/llvm_codegen_function.c | 10 +++++++++- src/compiler/sema_internal.h | 6 +++--- test/unit/stdlib/core/builtintests.c3 | 2 +- wrapper/include/c3_llvm.h | 1 + wrapper/src/wrapper.cpp | 5 +++++ 9 files changed, 30 insertions(+), 5 deletions(-) diff --git a/releasenotes.md b/releasenotes.md index 766c19abb..e783b45b5 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -15,6 +15,7 @@ - Change typedef and const enums to not convert from literals by default. - Add `@constinit` to allow old typedef behaviour. - Include actual element count in the error message when the array initializer size does not match the expected size. +- Add `--print-large-functions` for checking which functions likely dominate the compile time. ### Stdlib changes - Summarize sort macros as generic function wrappers to reduce the amount of generated code. #2831 diff --git a/src/build/build.h b/src/build/build.h index 883391380..d5034895b 100644 --- a/src/build/build.h +++ b/src/build/build.h @@ -210,6 +210,7 @@ typedef struct BuildOptions_ bool test_nosort; bool test_noleak; bool test_show_output; + bool print_large_functions; const char *custom_linker_path; uint32_t symtab_size; unsigned version; @@ -416,6 +417,7 @@ typedef struct bool old_enums; bool old_compact_eq; bool single_threaded; + bool print_large_functions; int build_threads; TrustLevel trust_level; OptimizationSetting optsetting; diff --git a/src/build/build_options.c b/src/build/build_options.c index 2d0a436e6..f6170d6ee 100644 --- a/src/build/build_options.c +++ b/src/build/build_options.c @@ -140,6 +140,7 @@ static void usage(bool full) print_opt("--use-old-slice-copy", "Use the old slice copy semantics."); print_opt("--use-old-enums", "Use the old enum syntax and semantics."); print_opt("--use-old-compact-eq", "Enable the old ability to use '@compact' to make a struct comparable."); + print_opt("--print-large-functions", "Print functions with large compile size."); print_opt("--warn-deadcode=", "Print warning on dead-code: yes, no, error."); print_opt("--warn-methodsnotresolved=", "Print warning on methods not resolved when accessed: yes, no, error."); print_opt("--warn-deprecation=", "Print warning when using deprecated code and constructs: yes, no, error."); @@ -895,6 +896,11 @@ static void parse_option(BuildOptions *options) options->test_nosort = true; return; } + if (match_longopt("print-large-functions")) + { + options->print_large_functions = true; + return; + } if ((argopt = match_argopt("warn-deadcode"))) { options->warnings.dead_code = parse_opt_select(WarningLevel, argopt, warnings); diff --git a/src/build/builder.c b/src/build/builder.c index 74e2c2018..d83e8651e 100644 --- a/src/build/builder.c +++ b/src/build/builder.c @@ -429,6 +429,8 @@ static void update_build_target_from_options(BuildTarget *target, BuildOptions * target->old_slice_copy = options->old_slice_copy; target->old_enums = options->old_enums; target->old_compact_eq = options->old_compact_eq; + + target->print_large_functions = options->print_large_functions; // Remove feature flags FOREACH(const char *, remove_feature, options->removed_feature_names) { diff --git a/src/compiler/llvm_codegen_function.c b/src/compiler/llvm_codegen_function.c index acb8d7484..1a5f94b94 100644 --- a/src/compiler/llvm_codegen_function.c +++ b/src/compiler/llvm_codegen_function.c @@ -609,7 +609,15 @@ void llvm_emit_body(GenContext *c, LLVMValueRef function, FunctionPrototype *pro c->debug.block_stack = NULL; LLVMDIBuilderFinalizeSubprogram(c->debug.builder, c->debug.function); } - + if (compiler.build.print_large_functions) + { + unsigned instruction_count = LLVMGetFunctionInstructionCount(function); + if (instruction_count > 5000) + { + eprintf("%8u instructions found in %s:%s (%s) - function is very long.\n", instruction_count, decl->unit->module->name->module, + decl->name, decl->unit->file->full_path); + } + } c->builder = prev_builder; c->cur_func.ref = prev_function; } diff --git a/src/compiler/sema_internal.h b/src/compiler/sema_internal.h index e9d4999ca..22b1214eb 100644 --- a/src/compiler/sema_internal.h +++ b/src/compiler/sema_internal.h @@ -51,14 +51,14 @@ const char *context_filename(SemaContext *context); AstId context_get_defers(SemaContext *context, AstId defer_bottom, bool is_success); void context_pop_defers(SemaContext *context, AstId *next); void context_pop_defers_and_replace_ast(SemaContext *context, Ast *ast); -void context_change_scope_for_label(SemaContext *context, DeclId label, SourceSpan span); +void context_change_scope_for_label(SemaContext *context, DeclId label_id, SourceSpan span); void context_change_scope_with_flags(SemaContext *context, ScopeFlags flags, SourceSpan span); SemaContext *context_transform_for_eval(SemaContext *context, SemaContext *temp_context, CompilationUnit *eval_unit); TokenType sema_splitpathref(const char *string, ArraySize len, Path **path_ref, const char **ident_ref); -void sema_print_inline(SemaContext *context, SourceSpan span_original); +void sema_print_inline(SemaContext *context, SourceSpan original); void sema_error_at(SemaContext *context, SourceSpan span, const char *message, ...); -bool sema_warn_at(SemaContext *context, SourceSpan span, WarningLevel warning, const char *message, ...); +bool sema_warn_at(SemaContext *context, SourceSpan span, WarningLevel level, const char *message, ...); void sema_context_init(SemaContext *context, CompilationUnit *unit); void sema_context_destroy(SemaContext *context); diff --git a/test/unit/stdlib/core/builtintests.c3 b/test/unit/stdlib/core/builtintests.c3 index fe0f12c4e..f1d5b031c 100644 --- a/test/unit/stdlib/core/builtintests.c3 +++ b/test/unit/stdlib/core/builtintests.c3 @@ -225,7 +225,7 @@ macro test_hash_vector_macro_bool() } $endforeach } -fn void test_hash_vector() +fn void test_hash_vector() @if($feature(SLOW_TESTS)) { test_hash_vector_macro(char, ichar, short, ushort, int, uint, long, ulong, int128, uint128); test_hash_vector_macro_bool(); diff --git a/wrapper/include/c3_llvm.h b/wrapper/include/c3_llvm.h index 342d6fce5..439893280 100644 --- a/wrapper/include/c3_llvm.h +++ b/wrapper/include/c3_llvm.h @@ -81,6 +81,7 @@ void LLVMSetDSOLocal(LLVMValueRef Global, bool value); void LLVMSetTargetMachineUseInitArray(LLVMTargetMachineRef ref, bool use_init_array); void LLVMSetTargetMachineEmulatedTLS(LLVMTargetMachineRef ref, bool emulated_tls); void LLVMSetNoSanitizeAddress(LLVMValueRef Global); +unsigned LLVMGetFunctionInstructionCount(LLVMValueRef function); #ifdef __cplusplus } diff --git a/wrapper/src/wrapper.cpp b/wrapper/src/wrapper.cpp index ef0c6f5b2..eedf03aab 100644 --- a/wrapper/src/wrapper.cpp +++ b/wrapper/src/wrapper.cpp @@ -341,6 +341,11 @@ void LLVMSetNoSanitizeAddress(LLVMValueRef Global) global->setSanitizerMetadata(data); } +unsigned LLVMGetFunctionInstructionCount(LLVMValueRef function) +{ + auto func = llvm::unwrap(function); + return func->getInstructionCount(); +} void LLVMBuilderSetFastMathFlags(LLVMBuilderRef Builder, FastMathOption option) { llvm::FastMathFlags math_flags {};