Skip to content

Commit c2bf802

Browse files
JeffBezansonsimeonschaub
authored andcommitted
more per-module compiler options (JuliaLang#37041)
1 parent 4c6b7f7 commit c2bf802

12 files changed

+145
-21
lines changed

NEWS.md

+2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ New language features
88
`(Foo{T} where T)(x) = ...`.
99
* `<--` and `<-->` are now available as infix operators, with the same precedence
1010
and associativity as other arrow-like operators ([#36666]).
11+
* Compilation and type inference can now be enabled or disabled at the module level
12+
using the experimental macro `Base.Experimental.@compiler_options` ([#37041]).
1113

1214
Language changes
1315
----------------

base/compiler/typeinfer.jl

+6
Original file line numberDiff line numberDiff line change
@@ -513,6 +513,9 @@ function typeinf_edge(interp::AbstractInterpreter, method::Method, @nospecialize
513513
return code.rettype, mi
514514
end
515515
end
516+
if ccall(:jl_get_module_infer, Cint, (Any,), method.module) == 0
517+
return Any, nothing
518+
end
516519
if !caller.cached && caller.parent === nothing
517520
# this caller exists to return to the user
518521
# (if we asked resolve_call_cyle, it might instead detect that there is a cycle that it can't merge)
@@ -617,6 +620,9 @@ function typeinf_ext(interp::AbstractInterpreter, mi::MethodInstance)
617620
end
618621
end
619622
end
623+
if ccall(:jl_get_module_infer, Cint, (Any,), method.module) == 0
624+
return retrieve_code_info(mi)
625+
end
620626
lock_mi_inference(interp, mi)
621627
frame = InferenceState(InferenceResult(mi), #=cached=#true, interp)
622628
frame === nothing && return nothing

base/experimental.jl

+40
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,46 @@ macro optlevel(n::Int)
120120
return Expr(:meta, :optlevel, n)
121121
end
122122

123+
"""
124+
Experimental.@compiler_options optimize={0,1,2,3} compile={yes,no,all,min} infer={yes,no}
125+
126+
Set compiler options for code in the enclosing module. Options correspond directly to
127+
command-line options with the same name, where applicable. The following options
128+
are currently supported:
129+
130+
* `optimize`: Set optimization level.
131+
* `compile`: Toggle native code compilation. Currently only `min` is supported, which
132+
requests the minimum possible amount of compilation.
133+
* `infer`: Enable or disable type inference. If disabled, implies [`@nospecialize`](@ref).
134+
"""
135+
macro compiler_options(args...)
136+
opts = Expr(:block)
137+
for ex in args
138+
if isa(ex, Expr) && ex.head === :(=) && length(ex.args) == 2
139+
if ex.args[1] === :optimize
140+
push!(opts.args, Expr(:meta, :optlevel, ex.args[2]::Int))
141+
elseif ex.args[1] === :compile
142+
a = ex.args[2]
143+
a = #a === :no ? 0 :
144+
#a === :yes ? 1 :
145+
#a === :all ? 2 :
146+
a === :min ? 3 : error("invalid argument to \"compile\" option")
147+
push!(opts.args, Expr(:meta, :compile, a))
148+
elseif ex.args[1] === :infer
149+
a = ex.args[2]
150+
a = a === false || a === :no ? 0 :
151+
a === true || a === :yes ? 1 : error("invalid argument to \"infer\" option")
152+
push!(opts.args, Expr(:meta, :infer, a))
153+
else
154+
error("unknown option \"$(ex.args[1])\"")
155+
end
156+
else
157+
error("invalid option syntax")
158+
end
159+
end
160+
return opts
161+
end
162+
123163
# UI features for errors
124164

125165
"""

src/ast.c

+3
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ jl_sym_t *coverageeffect_sym; jl_sym_t *escape_sym;
6464
jl_sym_t *aliasscope_sym; jl_sym_t *popaliasscope_sym;
6565
jl_sym_t *optlevel_sym; jl_sym_t *thismodule_sym;
6666
jl_sym_t *atom_sym; jl_sym_t *statement_sym; jl_sym_t *all_sym;
67+
jl_sym_t *compile_sym; jl_sym_t *infer_sym;
6768

6869
static uint8_t flisp_system_image[] = {
6970
#include <julia_flisp.boot.inc>
@@ -382,6 +383,8 @@ void jl_init_common_symbols(void)
382383
nospecialize_sym = jl_symbol("nospecialize");
383384
specialize_sym = jl_symbol("specialize");
384385
optlevel_sym = jl_symbol("optlevel");
386+
compile_sym = jl_symbol("compile");
387+
infer_sym = jl_symbol("infer");
385388
macrocall_sym = jl_symbol("macrocall");
386389
escape_sym = jl_symbol("escape");
387390
hygienicscope_sym = jl_symbol("hygienic-scope");

src/dump.c

+6-2
Original file line numberDiff line numberDiff line change
@@ -375,7 +375,9 @@ static void jl_serialize_module(jl_serializer_state *s, jl_module_t *m)
375375
write_uint64(s->s, m->build_id);
376376
write_int32(s->s, m->counter);
377377
write_int32(s->s, m->nospecialize);
378-
write_int32(s->s, m->optlevel);
378+
write_uint8(s->s, m->optlevel);
379+
write_uint8(s->s, m->compile);
380+
write_uint8(s->s, m->infer);
379381
}
380382

381383
static void jl_serialize_value_(jl_serializer_state *s, jl_value_t *v, int as_literal) JL_GC_DISABLED
@@ -1528,7 +1530,9 @@ static jl_value_t *jl_deserialize_value_module(jl_serializer_state *s) JL_GC_DIS
15281530
m->build_id = read_uint64(s->s);
15291531
m->counter = read_int32(s->s);
15301532
m->nospecialize = read_int32(s->s);
1531-
m->optlevel = read_int32(s->s);
1533+
m->optlevel = read_int8(s->s);
1534+
m->compile = read_int8(s->s);
1535+
m->infer = read_int8(s->s);
15321536
m->primary_world = jl_world_counter;
15331537
return (jl_value_t*)m;
15341538
}

src/gf.c

+13-4
Original file line numberDiff line numberDiff line change
@@ -1848,9 +1848,18 @@ jl_code_instance_t *jl_compile_method_internal(jl_method_instance_t *mi, size_t
18481848
jl_code_instance_t *codeinst = jl_method_compiled(mi, world);
18491849
if (codeinst)
18501850
return codeinst;
1851-
1852-
if (jl_options.compile_enabled == JL_OPTIONS_COMPILE_OFF ||
1853-
jl_options.compile_enabled == JL_OPTIONS_COMPILE_MIN) {
1851+
int compile_option = jl_options.compile_enabled;
1852+
jl_method_t *def = mi->def.method;
1853+
// disabling compilation per-module can override global setting
1854+
if (jl_is_method(def)) {
1855+
int mod_setting = jl_get_module_compile(((jl_method_t*)def)->module);
1856+
if (mod_setting == JL_OPTIONS_COMPILE_OFF ||
1857+
mod_setting == JL_OPTIONS_COMPILE_MIN)
1858+
compile_option = ((jl_method_t*)def)->module->compile;
1859+
}
1860+
1861+
if (compile_option == JL_OPTIONS_COMPILE_OFF ||
1862+
compile_option == JL_OPTIONS_COMPILE_MIN) {
18541863
// copy fptr from the template method definition
18551864
jl_method_t *def = mi->def.method;
18561865
if (jl_is_method(def) && def->unspecialized) {
@@ -1876,7 +1885,7 @@ jl_code_instance_t *jl_compile_method_internal(jl_method_instance_t *mi, size_t
18761885
jl_mi_cache_insert(mi, codeinst);
18771886
return codeinst;
18781887
}
1879-
if (jl_options.compile_enabled == JL_OPTIONS_COMPILE_OFF) {
1888+
if (compile_option == JL_OPTIONS_COMPILE_OFF) {
18801889
jl_printf(JL_STDERR, "code missing for ");
18811890
jl_static_show(JL_STDERR, (jl_value_t*)mi);
18821891
jl_printf(JL_STDERR, " : sysimg may not have been built with --compile=all\n");

src/interpreter.c

+16-4
Original file line numberDiff line numberDiff line change
@@ -536,10 +536,22 @@ static jl_value_t *eval_body(jl_array_t *stmts, interpreter_state *s, size_t ip,
536536
if (jl_expr_nargs(stmt) == 1 && jl_exprarg(stmt, 0) == (jl_value_t*)specialize_sym) {
537537
jl_set_module_nospecialize(s->module, 0);
538538
}
539-
if (jl_expr_nargs(stmt) == 2 && jl_exprarg(stmt, 0) == (jl_value_t*)optlevel_sym) {
540-
if (jl_is_long(jl_exprarg(stmt, 1))) {
541-
int n = jl_unbox_long(jl_exprarg(stmt, 1));
542-
jl_set_module_optlevel(s->module, n);
539+
if (jl_expr_nargs(stmt) == 2) {
540+
if (jl_exprarg(stmt, 0) == (jl_value_t*)optlevel_sym) {
541+
if (jl_is_long(jl_exprarg(stmt, 1))) {
542+
int n = jl_unbox_long(jl_exprarg(stmt, 1));
543+
jl_set_module_optlevel(s->module, n);
544+
}
545+
}
546+
else if (jl_exprarg(stmt, 0) == (jl_value_t*)compile_sym) {
547+
if (jl_is_long(jl_exprarg(stmt, 1))) {
548+
jl_set_module_compile(s->module, jl_unbox_long(jl_exprarg(stmt, 1)));
549+
}
550+
}
551+
else if (jl_exprarg(stmt, 0) == (jl_value_t*)infer_sym) {
552+
if (jl_is_long(jl_exprarg(stmt, 1))) {
553+
jl_set_module_infer(s->module, jl_unbox_long(jl_exprarg(stmt, 1)));
554+
}
543555
}
544556
}
545557
}

src/julia.h

+7-1
Original file line numberDiff line numberDiff line change
@@ -513,7 +513,9 @@ typedef struct _jl_module_t {
513513
size_t primary_world;
514514
uint32_t counter;
515515
int32_t nospecialize; // global bit flags: initialization for new methods
516-
int32_t optlevel;
516+
int8_t optlevel;
517+
int8_t compile;
518+
int8_t infer;
517519
uint8_t istopmod;
518520
jl_mutex_t lock;
519521
} jl_module_t;
@@ -1518,6 +1520,10 @@ JL_DLLEXPORT jl_module_t *jl_new_module(jl_sym_t *name);
15181520
JL_DLLEXPORT void jl_set_module_nospecialize(jl_module_t *self, int on);
15191521
JL_DLLEXPORT void jl_set_module_optlevel(jl_module_t *self, int lvl);
15201522
JL_DLLEXPORT int jl_get_module_optlevel(jl_module_t *m);
1523+
JL_DLLEXPORT void jl_set_module_compile(jl_module_t *self, int value);
1524+
JL_DLLEXPORT int jl_get_module_compile(jl_module_t *m);
1525+
JL_DLLEXPORT void jl_set_module_infer(jl_module_t *self, int value);
1526+
JL_DLLEXPORT int jl_get_module_infer(jl_module_t *m);
15211527
// get binding for reading
15221528
JL_DLLEXPORT jl_binding_t *jl_get_binding(jl_module_t *m JL_PROPAGATES_ROOT, jl_sym_t *var);
15231529
JL_DLLEXPORT jl_binding_t *jl_get_binding_or_error(jl_module_t *m, jl_sym_t *var);

src/julia_internal.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -1163,7 +1163,8 @@ extern jl_sym_t *colon_sym; extern jl_sym_t *hygienicscope_sym;
11631163
extern jl_sym_t *throw_undef_if_not_sym; extern jl_sym_t *getfield_undefref_sym;
11641164
extern jl_sym_t *gc_preserve_begin_sym; extern jl_sym_t *gc_preserve_end_sym;
11651165
extern jl_sym_t *coverageeffect_sym; extern jl_sym_t *escape_sym;
1166-
extern jl_sym_t *optlevel_sym;
1166+
extern jl_sym_t *optlevel_sym; extern jl_sym_t *compile_sym;
1167+
extern jl_sym_t *infer_sym;
11671168
extern jl_sym_t *atom_sym; extern jl_sym_t *statement_sym; extern jl_sym_t *all_sym;
11681169

11691170
struct _jl_sysimg_fptrs_t;

src/module.c

+35
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ JL_DLLEXPORT jl_module_t *jl_new_module(jl_sym_t *name)
3636
m->counter = 1;
3737
m->nospecialize = 0;
3838
m->optlevel = -1;
39+
m->compile = -1;
40+
m->infer = -1;
3941
JL_MUTEX_INIT(&m->lock);
4042
htable_new(&m->bindings, 0);
4143
arraylist_new(&m->usings, 0);
@@ -88,6 +90,39 @@ JL_DLLEXPORT int jl_get_module_optlevel(jl_module_t *m)
8890
return lvl;
8991
}
9092

93+
JL_DLLEXPORT void jl_set_module_compile(jl_module_t *self, int value)
94+
{
95+
self->compile = value;
96+
}
97+
98+
JL_DLLEXPORT int jl_get_module_compile(jl_module_t *m)
99+
{
100+
int value = m->compile;
101+
while (value == -1 && m->parent != m && m != jl_base_module) {
102+
m = m->parent;
103+
value = m->compile;
104+
}
105+
return value;
106+
}
107+
108+
JL_DLLEXPORT void jl_set_module_infer(jl_module_t *self, int value)
109+
{
110+
self->infer = value;
111+
// no reason to specialize if inference is off
112+
if (!value)
113+
jl_set_module_nospecialize(self, 1);
114+
}
115+
116+
JL_DLLEXPORT int jl_get_module_infer(jl_module_t *m)
117+
{
118+
int value = m->infer;
119+
while (value == -1 && m->parent != m && m != jl_base_module) {
120+
m = m->parent;
121+
value = m->infer;
122+
}
123+
return value;
124+
}
125+
91126
JL_DLLEXPORT void jl_set_istopmod(jl_module_t *self, uint8_t isprimary)
92127
{
93128
self->istopmod = 1;

src/precompile.c

+11-7
Original file line numberDiff line numberDiff line change
@@ -46,13 +46,17 @@ void jl_write_compiler_output(void)
4646
jl_value_t *f = jl_get_global((jl_module_t*)m, jl_symbol("__init__"));
4747
if (f) {
4848
jl_array_ptr_1d_push(jl_module_init_order, m);
49-
// TODO: this would be better handled if moved entirely to jl_precompile
50-
// since it's a slightly duplication of effort
51-
jl_value_t *tt = jl_is_type(f) ? (jl_value_t*)jl_wrap_Type(f) : jl_typeof(f);
52-
JL_GC_PUSH1(&tt);
53-
tt = (jl_value_t*)jl_apply_tuple_type_v(&tt, 1);
54-
jl_compile_hint((jl_tupletype_t*)tt);
55-
JL_GC_POP();
49+
int setting = jl_get_module_compile((jl_module_t*)m);
50+
if (setting != JL_OPTIONS_COMPILE_OFF &&
51+
setting != JL_OPTIONS_COMPILE_MIN) {
52+
// TODO: this would be better handled if moved entirely to jl_precompile
53+
// since it's a slightly duplication of effort
54+
jl_value_t *tt = jl_is_type(f) ? (jl_value_t*)jl_wrap_Type(f) : jl_typeof(f);
55+
JL_GC_PUSH1(&tt);
56+
tt = (jl_value_t*)jl_apply_tuple_type_v(&tt, 1);
57+
jl_compile_hint((jl_tupletype_t*)tt);
58+
JL_GC_POP();
59+
}
5660
}
5761
}
5862

src/toplevel.c

+4-2
Original file line numberDiff line numberDiff line change
@@ -816,7 +816,9 @@ jl_value_t *jl_toplevel_eval_flex(jl_module_t *JL_NONNULL m, jl_value_t *e, int
816816
jl_value_t *result;
817817
if (has_intrinsics || (!has_defs && fast && has_loops &&
818818
jl_options.compile_enabled != JL_OPTIONS_COMPILE_OFF &&
819-
jl_options.compile_enabled != JL_OPTIONS_COMPILE_MIN)) {
819+
jl_options.compile_enabled != JL_OPTIONS_COMPILE_MIN &&
820+
jl_get_module_compile(m) != JL_OPTIONS_COMPILE_OFF &&
821+
jl_get_module_compile(m) != JL_OPTIONS_COMPILE_MIN)) {
820822
// use codegen
821823
mfunc = method_instance_for_thunk(thk, m);
822824
jl_resolve_globals_in_ir((jl_array_t*)thk->code, m, NULL, 0);
@@ -826,7 +828,7 @@ jl_value_t *jl_toplevel_eval_flex(jl_module_t *JL_NONNULL m, jl_value_t *e, int
826828
// helps in common cases.
827829
size_t world = jl_world_counter;
828830
ptls->world_age = world;
829-
if (!has_defs) {
831+
if (!has_defs && jl_get_module_infer(m) != 0) {
830832
(void)jl_type_infer(mfunc, world, 0);
831833
}
832834
result = jl_invoke(/*func*/NULL, /*args*/NULL, /*nargs*/0, mfunc);

0 commit comments

Comments
 (0)