diff --git a/NEWS.md b/NEWS.md index 71014e1e57695..c1f669d46bd44 100644 --- a/NEWS.md +++ b/NEWS.md @@ -23,6 +23,9 @@ New language features - actual running time for the task (`Base.Experimental.task_running_time_ns`), and - wall-time for the task (`Base.Experimental.task_wall_time_ns`). - Support for Unicode 16 ([#56925]). +* `Base.Experimental.@min_optlevel level` sets a minimum optlevel (`--min_optlevel`) for a + module, ensuring that level of optimization even if the process is run at a lower optlevel + (`-O`) ([#]). Language changes ---------------- diff --git a/base/experimental.jl b/base/experimental.jl index 17871b4f346d6..7fe016b61765e 100644 --- a/base/experimental.jl +++ b/base/experimental.jl @@ -124,6 +124,27 @@ macro optlevel(n::Int) return Expr(:meta, :optlevel, n) end +""" + Experimental.@min_optlevel n::Int + +Set the minimum optimization level (equivalent to the `--min-optlevel` command line +argument) for code in the current module. Submodules inherit the setting of their +parent module. + +Supported values are 0, 1, 2, and 3. + +This sets a lower-bound for optimization level, such that the effective optimization +level, `o` is `@min_optlevel <= o <= @optlevel` and `--min-optlevel <= o <= -O`. + +NOTE: This min optimization level will only be applied to LLVM functions that are compiled +from this module. If a function defined in this module is *inlined* into a function defined +outside the module, that function will not inherit this min optimization level. Consider +whether you need to introduce `@noinline` if you require users to observe your min optlevel. +""" +macro min_optlevel(n::Int) + return Expr(:meta, :min_optlevel, n) +end + """ Experimental.@max_methods n::Int diff --git a/src/ast.c b/src/ast.c index 0f24d96393f2f..093032586c1ff 100644 --- a/src/ast.c +++ b/src/ast.c @@ -100,6 +100,7 @@ JL_DLLEXPORT jl_sym_t *jl_escape_sym; JL_DLLEXPORT jl_sym_t *jl_aliasscope_sym; JL_DLLEXPORT jl_sym_t *jl_popaliasscope_sym; JL_DLLEXPORT jl_sym_t *jl_optlevel_sym; +JL_DLLEXPORT jl_sym_t *jl_min_optlevel_sym; JL_DLLEXPORT jl_sym_t *jl_thismodule_sym; JL_DLLEXPORT jl_sym_t *jl_eval_sym; JL_DLLEXPORT jl_sym_t *jl_include_sym; @@ -392,6 +393,7 @@ void jl_init_common_symbols(void) jl_specialize_sym = jl_symbol("specialize"); jl_nospecializeinfer_sym = jl_symbol("nospecializeinfer"); jl_optlevel_sym = jl_symbol("optlevel"); + jl_min_optlevel_sym = jl_symbol("min_optlevel"); jl_compile_sym = jl_symbol("compile"); jl_force_compile_sym = jl_symbol("force_compile"); jl_infer_sym = jl_symbol("infer"); diff --git a/src/codegen.cpp b/src/codegen.cpp index 7cfcc52db29c7..a854c6a907026 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -8673,6 +8673,11 @@ static jl_llvm_functions_t static const char* const optLevelStrings[] = { "0", "1", "2", "3" }; FnAttrs.addAttribute("julia-optimization-level", optLevelStrings[optlevel]); } + int min_optlevel = jl_get_module_min_optlevel(ctx.module); + if (min_optlevel >= 0 && min_optlevel <= 3) { + static const char* const optLevelStrings[] = { "0", "1", "2", "3" }; + FnAttrs.addAttribute("julia-min-optimization-level", optLevelStrings[min_optlevel]); + } ctx.f = f; diff --git a/src/interpreter.c b/src/interpreter.c index 338853b56f692..79e54e206d246 100644 --- a/src/interpreter.c +++ b/src/interpreter.c @@ -659,6 +659,12 @@ static jl_value_t *eval_body(jl_array_t *stmts, interpreter_state *s, size_t ip, jl_set_module_optlevel(s->module, n); } } + else if (jl_exprarg(stmt, 0) == (jl_value_t*)jl_min_optlevel_sym) { + if (jl_is_long(jl_exprarg(stmt, 1))) { + int n = jl_unbox_long(jl_exprarg(stmt, 1)); + jl_set_module_min_optlevel(s->module, n); + } + } else if (jl_exprarg(stmt, 0) == (jl_value_t*)jl_compile_sym) { if (jl_is_long(jl_exprarg(stmt, 1))) { jl_set_module_compile(s->module, jl_unbox_long(jl_exprarg(stmt, 1))); diff --git a/src/jitlayers.cpp b/src/jitlayers.cpp index 0acb7beaca9ab..5eac5a6fb2098 100644 --- a/src/jitlayers.cpp +++ b/src/jitlayers.cpp @@ -1049,6 +1049,14 @@ static orc::ThreadSafeModule selectOptLevel(orc::ThreadSafeModule TSM) JL_NOTSAF if (ol < opt_level) opt_level = ol; } + attr = F.getFnAttribute("julia-min-optimization-level"); + val = attr.getValueAsString(); + if (val != "") { + size_t ol = (size_t)val[0] - '0'; + if (ol > opt_level) + opt_level = ol; + jl_safe_printf("Function %s opt: %d\n", F.getName().str().c_str(), (int)opt_level); + } } } if (opt_level < opt_level_min) diff --git a/src/jl_exported_funcs.inc b/src/jl_exported_funcs.inc index c1b29a091511b..e52c27f9def39 100644 --- a/src/jl_exported_funcs.inc +++ b/src/jl_exported_funcs.inc @@ -213,6 +213,7 @@ XX(jl_get_module_infer) \ XX(jl_get_module_of_binding) \ XX(jl_get_module_optlevel) \ + XX(jl_get_module_min_optlevel) \ XX(jl_get_next_task) \ XX(jl_get_nth_field) \ XX(jl_get_nth_field_checked) \ @@ -411,6 +412,7 @@ XX(jl_set_module_infer) \ XX(jl_set_module_nospecialize) \ XX(jl_set_module_optlevel) \ + XX(jl_set_module_min_optlevel) \ XX(jl_set_module_uuid) \ XX(jl_set_next_task) \ XX(jl_set_nth_field) \ diff --git a/src/julia.h b/src/julia.h index b5416568b7ae9..554ea1ddd333d 100644 --- a/src/julia.h +++ b/src/julia.h @@ -724,6 +724,7 @@ typedef struct _jl_module_t { _Atomic(uint32_t) counter; int32_t nospecialize; // global bit flags: initialization for new methods int8_t optlevel; + int8_t min_optlevel; int8_t compile; int8_t infer; uint8_t istopmod; @@ -1989,7 +1990,9 @@ extern JL_DLLIMPORT jl_module_t *jl_libdl_module JL_GLOBALLY_ROOTED; JL_DLLEXPORT jl_module_t *jl_new_module(jl_sym_t *name, jl_module_t *parent); JL_DLLEXPORT void jl_set_module_nospecialize(jl_module_t *self, int on); JL_DLLEXPORT void jl_set_module_optlevel(jl_module_t *self, int lvl); +JL_DLLEXPORT void jl_set_module_min_optlevel(jl_module_t *self, int lvl); JL_DLLEXPORT int jl_get_module_optlevel(jl_module_t *m); +JL_DLLEXPORT int jl_get_module_min_optlevel(jl_module_t *m); JL_DLLEXPORT void jl_set_module_compile(jl_module_t *self, int value); JL_DLLEXPORT int jl_get_module_compile(jl_module_t *m); JL_DLLEXPORT void jl_set_module_infer(jl_module_t *self, int value); diff --git a/src/julia_internal.h b/src/julia_internal.h index 3e4967c9d4dca..ca2c6f5d4955a 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -1867,6 +1867,7 @@ extern JL_DLLEXPORT jl_sym_t *jl_escape_sym; extern JL_DLLEXPORT jl_sym_t *jl_aliasscope_sym; extern JL_DLLEXPORT jl_sym_t *jl_popaliasscope_sym; extern JL_DLLEXPORT jl_sym_t *jl_optlevel_sym; +extern JL_DLLEXPORT jl_sym_t *jl_min_optlevel_sym; extern JL_DLLEXPORT jl_sym_t *jl_thismodule_sym; extern JL_DLLEXPORT jl_sym_t *jl_eval_sym; extern JL_DLLEXPORT jl_sym_t *jl_include_sym; diff --git a/src/module.c b/src/module.c index 66049031f8790..a07ecb9e84c8a 100644 --- a/src/module.c +++ b/src/module.c @@ -91,6 +91,7 @@ JL_DLLEXPORT jl_module_t *jl_new_module_(jl_sym_t *name, jl_module_t *parent, ui jl_atomic_store_relaxed(&m->counter, 1); m->nospecialize = 0; m->optlevel = -1; + m->min_optlevel = -1; m->compile = -1; m->infer = -1; m->max_methods = -1; @@ -145,6 +146,10 @@ JL_DLLEXPORT void jl_set_module_optlevel(jl_module_t *self, int lvl) { self->optlevel = lvl; } +JL_DLLEXPORT void jl_set_module_min_optlevel(jl_module_t *self, int lvl) +{ + self->min_optlevel = lvl; +} JL_DLLEXPORT int jl_get_module_optlevel(jl_module_t *m) { @@ -155,6 +160,16 @@ JL_DLLEXPORT int jl_get_module_optlevel(jl_module_t *m) } return lvl; } +JL_DLLEXPORT int jl_get_module_min_optlevel(jl_module_t *m) +{ + int lvl = m->min_optlevel; + while (lvl == -1 && m->parent != m && m != jl_base_module) { + m = m->parent; + lvl = m->min_optlevel; + } + return lvl; +} + JL_DLLEXPORT void jl_set_module_compile(jl_module_t *self, int value) {