Skip to content

Commit

Permalink
introduce @nospecializeinfer macro to tell the compiler to avoid ex…
Browse files Browse the repository at this point in the history
…cess inference

This commit introduces a new compiler annotation called `@nospecializeinfer`,
which allows us to request the compiler to avoid excessive inference.

\## `@nospecialize` mechanism

T discuss `@nospecializeinfer`, let's first understand the behavior of
`@nospecialize`.

Its docstring says that

> This is only a hint for the compiler to avoid excess code generation.

, and it works by suppressing dispatches with complex runtime
occurrences of the annotated arguments. This could be understood with
the example below:
```julia
julia> function call_func_itr(func, itr)
           local r = 0
           r += func(itr[1])
           r += func(itr[2])
           r += func(itr[3])
           r
       end;

julia> _isa = isa; # just for the sake of explanation, global variable to prevent inlining

julia> func_specialize(a) = _isa(a, Function);

julia> func_nospecialize(@nospecialize a) = _isa(a, Function);

julia> dispatchonly = Any[sin, muladd, nothing]; # untyped container can cause excessive runtime dispatch

julia> @code_typed call_func_itr(func_specialize, dispatchonly)
CodeInfo(
1 ─ %1  = π (0, Int64)
│   %2  = Base.arrayref(true, itr, 1)::Any
│   %3  = (func)(%2)::Any
│   %4  = (%1 + %3)::Any
│   %5  = Base.arrayref(true, itr, 2)::Any
│   %6  = (func)(%5)::Any
│   %7  = (%4 + %6)::Any
│   %8  = Base.arrayref(true, itr, 3)::Any
│   %9  = (func)(%8)::Any
│   %10 = (%7 + %9)::Any
└──       return %10
) => Any

julia> @code_typed call_func_itr(func_nospecialize, dispatchonly)
CodeInfo(
1 ─ %1  = π (0, Int64)
│   %2  = Base.arrayref(true, itr, 1)::Any
│   %3  = invoke func(%2::Any)::Any
│   %4  = (%1 + %3)::Any
│   %5  = Base.arrayref(true, itr, 2)::Any
│   %6  = invoke func(%5::Any)::Any
│   %7  = (%4 + %6)::Any
│   %8  = Base.arrayref(true, itr, 3)::Any
│   %9  = invoke func(%8::Any)::Any
│   %10 = (%7 + %9)::Any
└──       return %10
) => Any
```

The calls of `func_specialize` remain to be `:call` expression (so that
they are dispatched and compiled at runtime) while the calls of
`func_nospecialize` are resolved as `:invoke` expressions. This is
because `@nospecialize` requests the compiler to give up compiling
`func_nospecialize` with runtime argument types but with the declared
argument types, allowing `call_func_itr(func_nospecialize, dispatchonly)`
to avoid runtime dispatches and accompanying JIT compilations
(i.e. "excess code generation").

The difference is evident when checking `specializations`:
```julia
julia> call_func_itr(func_specialize, dispatchonly)
2

julia> length(Base.specializations(only(methods(func_specialize))))
3 # w/ runtime dispatch, multiple specializations

julia> call_func_itr(func_nospecialize, dispatchonly)
2

julia> length(Base.specializations(only(methods(func_nospecialize))))
1 # w/o runtime dispatch, the single specialization
```

The problem here is that it influences dispatch only, and does not
intervene into inference in anyway. So there is still a possibility of
"excess inference" when the compiler sees a considerable complexity of
argument types during inference:
```julia
julia> func_specialize(a) = _isa(a, Function); # redefine func to clear the specializations

julia> @Assert length(Base.specializations(only(methods(func_specialize)))) == 0;

julia> func_nospecialize(@nospecialize a) = _isa(a, Function); # redefine func to clear the specializations

julia> @Assert length(Base.specializations(only(methods(func_nospecialize)))) == 0;

julia> withinfernce = tuple(sin, muladd, "foo"); # typed container can cause excessive inference

julia> @time @code_typed call_func_itr(func_specialize, withinfernce);
  0.000812 seconds (3.77 k allocations: 217.938 KiB, 94.34% compilation time)

julia> length(Base.specializations(only(methods(func_specialize))))
4 # multiple method instances inferred

julia> @time @code_typed call_func_itr(func_nospecialize, withinfernce);
  0.000753 seconds (3.77 k allocations: 218.047 KiB, 92.42% compilation time)

julia> length(Base.specializations(only(methods(func_nospecialize))))
4 # multiple method instances inferred
```

The purpose of this PR is to implement a mechanism that allows us to
avoid excessive inference to reduce the compilation latency when
inference sees a considerable complexity of argument types.

\## Design

Here are some ideas to implement the functionality:
1. make `@nospecialize` block inference
2. add nospecializeinfer effect when `@nospecialize`d method is annotated as `@noinline`
3. implement as `@pure`-like boolean annotation to request nospecializeinfer effect on top of `@nospecialize`
4. implement as annotation that is orthogonal to `@nospecialize`

After trying 1 ~ 3., I decided to submit 3.

\### 1. make `@nospecialize` block inference

This is almost same as what Jameson has done at <vtjnash@8ab7b6b>.
It turned out that this approach performs very badly because some of
`@nospecialize`'d arguments still need inference to perform reasonably.
For example, it's obvious that the following definition of
`getindex(@nospecialize(t::Tuple), i::Int)` would perform very badly if
`@nospecialize` blocks inference, because of a lack of useful type
information for succeeding optimizations:
<https://github.com/JuliaLang/julia/blob/12d364e8249a07097a233ce7ea2886002459cc50/base/tuple.jl#L29-L30>

\### 2. add nospecializeinfer effect when `@nospecialize`d method is annotated as `@noinline`

The important observation is that we often use `@nospecialize` even when
we expect inference to forward type and constant information.
Adversely, we may be able to exploit the fact that we usually don't
expect inference to forward information to a callee when we annotate it
with `@noinline` (i.e. when adding `@noinline`, we're usually fine with
disabling inter-procedural optimizations other than resolving dispatch).
So the idea is to enable the inference suppression when `@nospecialize`'d
method is annotated as `@noinline` too.

It's a reasonable choice and can be efficiently implemented with #41922.
But it sounds a bit weird to me to associate no infer effect with
`@noinline`, and I also think there may be some cases we want to inline
a method while partly avoiding inference, e.g.:
```julia
\# the compiler will always infer with `f::Any`
@noinline function twof(@nospecialize(f), n) # this method body is very simple and should be eligible for inlining
    if occursin('+', string(typeof(f).name.name::Symbol))
        2 + n
    elseif occursin('*', string(typeof(f).name.name::Symbol))
        2n
    else
        zero(n)
    end
end
```

\### 3. implement as `@pure`-like boolean annotation to request nospecializeinfer effect on top of `@nospecialize`

This is what this commit implements. It basically replaces the previous
`@noinline` flag with a newly-introduced annotation named `@nospecializeinfer`.
It is still associated with `@nospecialize` and it only has effect when
used together with `@nospecialize`, but now it is not associated to
`@noinline`, and it would help us reason about the behavior of `@nospecializeinfer`
and experiment its effect more safely:
```julia
\# the compiler will always infer with `f::Any`
Base.@nospecializeinfer function twof(@nospecialize(f), n) # the compiler may or not inline this method
    if occursin('+', string(typeof(f).name.name::Symbol))
        2 + n
    elseif occursin('*', string(typeof(f).name.name::Symbol))
        2n
    else
        zero(n)
    end
end
```

\### 4. implement as annotation that is orthogonal to `@nospecialize`

Actually, we can have `@nospecialize` and `@nospecializeinfer` separately, and it
would allow us to configure compilation strategies in a more
fine-grained way.
```julia
function noinfspec(Base.@nospecializeinfer(f), @nospecialize(g))
    ...
end
```

I'm fine with this approach but at the same time I'm afraid to have too
many annotations that are related to some sort (I expect we will
annotate both `@nospecializeinfer` and `@nospecialize` in this scheme).

Co-authored-by: Mosè Giordano <giordano@users.noreply.github.com>
Co-authored-by: Tim Holy <tim.holy@gmail.com>
  • Loading branch information
3 people committed May 10, 2023
1 parent b9b8b38 commit 63dab32
Show file tree
Hide file tree
Showing 15 changed files with 216 additions and 31 deletions.
4 changes: 4 additions & 0 deletions base/compiler/abstractinterpretation.jl
Original file line number Diff line number Diff line change
Expand Up @@ -521,6 +521,10 @@ function abstract_call_method(interp::AbstractInterpreter,
sigtuple = unwrap_unionall(sig)
sigtuple isa DataType || return MethodCallResult(Any, false, false, nothing, Effects())

if is_nospecializeinfer(method)
sig = get_nospecializeinfer_sig(method, sig, sparams)
end

# Limit argument type tuple growth of functions:
# look through the parents list to see if there's a call to the same method
# and from the same method.
Expand Down
19 changes: 17 additions & 2 deletions base/compiler/utilities.jl
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,10 @@ function is_inlineable_constant(@nospecialize(x))
return count_const_size(x) <= MAX_INLINE_CONST_SIZE
end

is_nospecialized(method::Method) = method.nospecialize 0

is_nospecializeinfer(method::Method) = method.nospecializeinfer && is_nospecialized(method)

###########################
# MethodInstance/CodeInfo #
###########################
Expand Down Expand Up @@ -154,8 +158,16 @@ function get_compileable_sig(method::Method, @nospecialize(atype), sparams::Simp
isa(atype, DataType) || return nothing
mt = ccall(:jl_method_get_table, Any, (Any,), method)
mt === nothing && return nothing
return ccall(:jl_normalize_to_compilable_sig, Any, (Any, Any, Any, Any),
mt, atype, sparams, method)
return ccall(:jl_normalize_to_compilable_sig, Any, (Any, Any, Any, Any, Cint),
mt, atype, sparams, method, #=int return_if_compileable=#1)
end

function get_nospecializeinfer_sig(method::Method, @nospecialize(atype), sparams::SimpleVector)
isa(atype, DataType) || return method.sig
mt = ccall(:jl_method_table_for, Any, (Any,), atype)
mt === nothing && return method.sig
return ccall(:jl_normalize_to_compilable_sig, Any, (Any, Any, Any, Any, Cint),
mt, atype, sparams, method, #=int return_if_compileable=#0)
end

isa_compileable_sig(@nospecialize(atype), sparams::SimpleVector, method::Method) =
Expand Down Expand Up @@ -203,6 +215,9 @@ function specialize_method(method::Method, @nospecialize(atype), sparams::Simple
if isa(atype, UnionAll)
atype, sparams = normalize_typevars(method, atype, sparams)
end
if is_nospecializeinfer(method)
atype = get_nospecializeinfer_sig(method, atype, sparams)
end
if preexisting
# check cached specializations
# for an existing result stored there
Expand Down
3 changes: 2 additions & 1 deletion base/essentials.jl
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,8 @@ f(y) = [x for x in y]
!!! note
`@nospecialize` affects code generation but not inference: it limits the diversity
of the resulting native code, but it does not impose any limitations (beyond the
standard ones) on type-inference.
standard ones) on type-inference. Use [`Base.@nospecializeinfer`](@ref) together with
`@nospecialize` to additionally suppress inference.
# Example
Expand Down
39 changes: 38 additions & 1 deletion base/expr.jl
Original file line number Diff line number Diff line change
Expand Up @@ -342,7 +342,6 @@ macro noinline(x)
return annotate_meta_def_or_block(x, :noinline)
end


"""
@constprop setting [ex]
Expand Down Expand Up @@ -763,6 +762,44 @@ function compute_assumed_setting(@nospecialize(setting), val::Bool=true)
end
end

"""
Base.@nospecializeinfer function f(args...)
@nospecialize ...
...
end
Base.@nospecializeinfer f(@nospecialize args...) = ...
Tells the compiler to infer `f` using the declared types of `@nospecialize`d arguments.
This can be used to limit the number of compiler-generated specializations during inference.
# Example
```julia
julia> f(A::AbstractArray) = g(A)
f (generic function with 1 method)
julia> @noinline Base.@nospecializeinfer g(@nospecialize(A::AbstractArray)) = A[1]
g (generic function with 1 method)
julia> @code_typed f([1.0])
CodeInfo(
1 ─ %1 = invoke Main.g(_2::AbstractArray)::Any
└── return %1
) => Any
```
In this example, `f` will be inferred for each specific type of `A`,
but `g` will only be inferred once with the declared argument type `A::AbstractArray`,
meaning that the compiler will not likely see the excessive inference time on it
while it can not infer the concrete return type of it.
Without the `@nospecializeinfer`, `f([1.0])` would infer the return type of `g` as `Float64`,
indicating that inference ran for `g(::Vector{Float64})` despite the prohibition on
specialized code generation.
"""
macro nospecializeinfer(ex)
esc(isa(ex, Expr) ? pushmeta!(ex, :nospecializeinfer) : ex)
end

"""
@propagate_inbounds
Expand Down
2 changes: 2 additions & 0 deletions doc/src/base/base.md
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,8 @@ Base.@inline
Base.@noinline
Base.@nospecialize
Base.@specialize
Base.@nospecializeinfer
Base.@constprop
Base.gensym
Base.@gensym
var"name"
Expand Down
2 changes: 2 additions & 0 deletions src/ast.c
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ JL_DLLEXPORT jl_sym_t *jl_aggressive_constprop_sym;
JL_DLLEXPORT jl_sym_t *jl_no_constprop_sym;
JL_DLLEXPORT jl_sym_t *jl_purity_sym;
JL_DLLEXPORT jl_sym_t *jl_nospecialize_sym;
JL_DLLEXPORT jl_sym_t *jl_nospecializeinfer_sym;
JL_DLLEXPORT jl_sym_t *jl_macrocall_sym;
JL_DLLEXPORT jl_sym_t *jl_colon_sym;
JL_DLLEXPORT jl_sym_t *jl_hygienicscope_sym;
Expand Down Expand Up @@ -342,6 +343,7 @@ void jl_init_common_symbols(void)
jl_isdefined_sym = jl_symbol("isdefined");
jl_nospecialize_sym = jl_symbol("nospecialize");
jl_specialize_sym = jl_symbol("specialize");
jl_nospecializeinfer_sym = jl_symbol("nospecializeinfer");
jl_optlevel_sym = jl_symbol("optlevel");
jl_compile_sym = jl_symbol("compile");
jl_force_compile_sym = jl_symbol("force_compile");
Expand Down
9 changes: 5 additions & 4 deletions src/gf.c
Original file line number Diff line number Diff line change
Expand Up @@ -2564,7 +2564,8 @@ JL_DLLEXPORT int32_t jl_invoke_api(jl_code_instance_t *codeinst)
return -1;
}

JL_DLLEXPORT jl_value_t *jl_normalize_to_compilable_sig(jl_methtable_t *mt, jl_tupletype_t *ti, jl_svec_t *env, jl_method_t *m)
JL_DLLEXPORT jl_value_t *jl_normalize_to_compilable_sig(jl_methtable_t *mt, jl_tupletype_t *ti, jl_svec_t *env, jl_method_t *m,
int return_if_compileable)
{
jl_tupletype_t *tt = NULL;
jl_svec_t *newparams = NULL;
Expand All @@ -2588,7 +2589,7 @@ JL_DLLEXPORT jl_value_t *jl_normalize_to_compilable_sig(jl_methtable_t *mt, jl_t
if (!is_compileable)
is_compileable = jl_isa_compileable_sig(tt, env, m);
JL_GC_POP();
return is_compileable ? (jl_value_t*)tt : jl_nothing;
return (!return_if_compileable || is_compileable) ? (jl_value_t*)tt : jl_nothing;
}

jl_method_instance_t *jl_normalize_to_compilable_mi(jl_method_instance_t *mi JL_PROPAGATES_ROOT)
Expand All @@ -2599,7 +2600,7 @@ jl_method_instance_t *jl_normalize_to_compilable_mi(jl_method_instance_t *mi JL_
jl_methtable_t *mt = jl_method_get_table(def);
if ((jl_value_t*)mt == jl_nothing)
return mi;
jl_value_t *compilationsig = jl_normalize_to_compilable_sig(mt, (jl_datatype_t*)mi->specTypes, mi->sparam_vals, def);
jl_value_t *compilationsig = jl_normalize_to_compilable_sig(mt, (jl_datatype_t*)mi->specTypes, mi->sparam_vals, def, 1);
if (compilationsig == jl_nothing || jl_egal(compilationsig, mi->specTypes))
return mi;
jl_svec_t *env = NULL;
Expand Down Expand Up @@ -2632,7 +2633,7 @@ jl_method_instance_t *jl_method_match_to_mi(jl_method_match_t *match, size_t wor
JL_UNLOCK(&mt->writelock);
}
else {
jl_value_t *tt = jl_normalize_to_compilable_sig(mt, ti, env, m);
jl_value_t *tt = jl_normalize_to_compilable_sig(mt, ti, env, m, 1);
if (tt != jl_nothing) {
JL_GC_PUSH2(&tt, &env);
if (!jl_egal(tt, (jl_value_t*)ti)) {
Expand Down
10 changes: 6 additions & 4 deletions src/ircode.c
Original file line number Diff line number Diff line change
Expand Up @@ -434,13 +434,14 @@ static void jl_encode_value_(jl_ircode_state *s, jl_value_t *v, int as_literal)
}
}

static jl_code_info_flags_t code_info_flags(uint8_t inferred, uint8_t propagate_inbounds,
uint8_t has_fcall, uint8_t inlining, uint8_t constprop)
static jl_code_info_flags_t code_info_flags(uint8_t inferred, uint8_t propagate_inbounds, uint8_t has_fcall,
uint8_t nospecializeinfer, uint8_t inlining, uint8_t constprop)
{
jl_code_info_flags_t flags;
flags.bits.inferred = inferred;
flags.bits.propagate_inbounds = propagate_inbounds;
flags.bits.has_fcall = has_fcall;
flags.bits.nospecializeinfer = nospecializeinfer;
flags.bits.inlining = inlining;
flags.bits.constprop = constprop;
return flags;
Expand Down Expand Up @@ -785,8 +786,8 @@ JL_DLLEXPORT jl_string_t *jl_compress_ir(jl_method_t *m, jl_code_info_t *code)
1
};

jl_code_info_flags_t flags = code_info_flags(code->inferred, code->propagate_inbounds,
code->has_fcall, code->inlining, code->constprop);
jl_code_info_flags_t flags = code_info_flags(code->inferred, code->propagate_inbounds, code->has_fcall,
code->nospecializeinfer, code->inlining, code->constprop);
write_uint8(s.s, flags.packed);
write_uint8(s.s, code->purity.bits);
write_uint16(s.s, code->inlining_cost);
Expand Down Expand Up @@ -885,6 +886,7 @@ JL_DLLEXPORT jl_code_info_t *jl_uncompress_ir(jl_method_t *m, jl_code_instance_t
code->inferred = flags.bits.inferred;
code->propagate_inbounds = flags.bits.propagate_inbounds;
code->has_fcall = flags.bits.has_fcall;
code->nospecializeinfer = flags.bits.nospecializeinfer;
code->purity.bits = read_uint8(s.s);
code->inlining_cost = read_uint16(s.s);

Expand Down
14 changes: 9 additions & 5 deletions src/jltypes.c
Original file line number Diff line number Diff line change
Expand Up @@ -2903,7 +2903,7 @@ void jl_init_types(void) JL_GC_DISABLED
jl_code_info_type =
jl_new_datatype(jl_symbol("CodeInfo"), core,
jl_any_type, jl_emptysvec,
jl_perm_symsvec(21,
jl_perm_symsvec(22,
"code",
"codelocs",
"ssavaluetypes",
Expand All @@ -2921,11 +2921,12 @@ void jl_init_types(void) JL_GC_DISABLED
"inferred",
"propagate_inbounds",
"has_fcall",
"nospecializeinfer",
"inlining",
"constprop",
"purity",
"inlining_cost"),
jl_svec(21,
jl_svec(22,
jl_array_any_type,
jl_array_int32_type,
jl_any_type,
Expand All @@ -2943,17 +2944,18 @@ void jl_init_types(void) JL_GC_DISABLED
jl_bool_type,
jl_bool_type,
jl_bool_type,
jl_bool_type,
jl_uint8_type,
jl_uint8_type,
jl_uint8_type,
jl_uint16_type),
jl_emptysvec,
0, 1, 20);
0, 1, 22);

jl_method_type =
jl_new_datatype(jl_symbol("Method"), core,
jl_any_type, jl_emptysvec,
jl_perm_symsvec(29,
jl_perm_symsvec(30,
"name",
"module",
"file",
Expand All @@ -2980,10 +2982,11 @@ void jl_init_types(void) JL_GC_DISABLED
"nkw",
"isva",
"is_for_opaque_closure",
"nospecializeinfer",
"constprop",
"max_varargs",
"purity"),
jl_svec(29,
jl_svec(30,
jl_symbol_type,
jl_module_type,
jl_symbol_type,
Expand All @@ -3010,6 +3013,7 @@ void jl_init_types(void) JL_GC_DISABLED
jl_int32_type,
jl_bool_type,
jl_bool_type,
jl_bool_type,
jl_uint8_type,
jl_uint8_type,
jl_uint8_type),
Expand Down
2 changes: 2 additions & 0 deletions src/julia.h
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,7 @@ typedef struct _jl_code_info_t {
uint8_t inferred;
uint8_t propagate_inbounds;
uint8_t has_fcall;
uint8_t nospecializeinfer;
// uint8 settings
uint8_t inlining; // 0 = default; 1 = @inline; 2 = @noinline
uint8_t constprop; // 0 = use heuristic; 1 = aggressive; 2 = none
Expand Down Expand Up @@ -356,6 +357,7 @@ typedef struct _jl_method_t {
// various boolean properties
uint8_t isva;
uint8_t is_for_opaque_closure;
uint8_t nospecializeinfer;
// uint8 settings
uint8_t constprop; // 0x00 = use heuristic; 0x01 = aggressive; 0x02 = none
uint8_t max_varargs; // 0xFF = use heuristic; otherwise, max # of args to expand
Expand Down
2 changes: 2 additions & 0 deletions src/julia_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -603,6 +603,7 @@ typedef struct {
uint8_t inferred:1;
uint8_t propagate_inbounds:1;
uint8_t has_fcall:1;
uint8_t nospecializeinfer:1;
uint8_t inlining:2; // 0 = use heuristic; 1 = aggressive; 2 = none
uint8_t constprop:2; // 0 = use heuristic; 1 = aggressive; 2 = none
} jl_code_info_flags_bitfield_t;
Expand Down Expand Up @@ -1578,6 +1579,7 @@ extern JL_DLLEXPORT jl_sym_t *jl_aggressive_constprop_sym;
extern JL_DLLEXPORT jl_sym_t *jl_no_constprop_sym;
extern JL_DLLEXPORT jl_sym_t *jl_purity_sym;
extern JL_DLLEXPORT jl_sym_t *jl_nospecialize_sym;
extern JL_DLLEXPORT jl_sym_t *jl_nospecializeinfer_sym;
extern JL_DLLEXPORT jl_sym_t *jl_macrocall_sym;
extern JL_DLLEXPORT jl_sym_t *jl_colon_sym;
extern JL_DLLEXPORT jl_sym_t *jl_hygienicscope_sym;
Expand Down
5 changes: 5 additions & 0 deletions src/method.c
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,8 @@ static void jl_code_info_set_ir(jl_code_info_t *li, jl_expr_t *ir)
li->inlining = 2;
else if (ma == (jl_value_t*)jl_propagate_inbounds_sym)
li->propagate_inbounds = 1;
else if (ma == (jl_value_t*)jl_nospecializeinfer_sym)
li->nospecializeinfer = 1;
else if (ma == (jl_value_t*)jl_aggressive_constprop_sym)
li->constprop = 1;
else if (ma == (jl_value_t*)jl_no_constprop_sym)
Expand Down Expand Up @@ -477,6 +479,7 @@ JL_DLLEXPORT jl_code_info_t *jl_new_code_info_uninit(void)
src->inferred = 0;
src->propagate_inbounds = 0;
src->has_fcall = 0;
src->nospecializeinfer = 0;
src->edges = jl_nothing;
src->constprop = 0;
src->inlining = 0;
Expand Down Expand Up @@ -682,6 +685,7 @@ static void jl_method_set_source(jl_method_t *m, jl_code_info_t *src)
}
}
m->called = called;
m->nospecializeinfer = src->nospecializeinfer;
m->constprop = src->constprop;
m->purity.bits = src->purity.bits;
jl_add_function_to_lineinfo(src, (jl_value_t*)m->name);
Expand Down Expand Up @@ -811,6 +815,7 @@ JL_DLLEXPORT jl_method_t *jl_new_method_uninit(jl_module_t *module)
m->primary_world = 1;
m->deleted_world = ~(size_t)0;
m->is_for_opaque_closure = 0;
m->nospecializeinfer = 0;
m->constprop = 0;
m->purity.bits = 0;
m->max_varargs = UINT8_MAX;
Expand Down
11 changes: 10 additions & 1 deletion stdlib/Serialization/src/Serialization.jl
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ const TAGS = Any[
const NTAGS = length(TAGS)
@assert NTAGS == 255

const ser_version = 23 # do not make changes without bumping the version #!
const ser_version = 24 # do not make changes without bumping the version #!

format_version(::AbstractSerializer) = ser_version
format_version(s::Serializer) = s.version
Expand Down Expand Up @@ -418,6 +418,7 @@ function serialize(s::AbstractSerializer, meth::Method)
serialize(s, meth.nargs)
serialize(s, meth.isva)
serialize(s, meth.is_for_opaque_closure)
serialize(s, meth.nospecializeinfer)
serialize(s, meth.constprop)
serialize(s, meth.purity)
if isdefined(meth, :source)
Expand Down Expand Up @@ -1026,10 +1027,14 @@ function deserialize(s::AbstractSerializer, ::Type{Method})
nargs = deserialize(s)::Int32
isva = deserialize(s)::Bool
is_for_opaque_closure = false
nospecializeinfer = false
constprop = purity = 0x00
template_or_is_opaque = deserialize(s)
if isa(template_or_is_opaque, Bool)
is_for_opaque_closure = template_or_is_opaque
if format_version(s) >= 24
nospecializeinfer = deserialize(s)::Bool
end
if format_version(s) >= 14
constprop = deserialize(s)::UInt8
end
Expand All @@ -1054,6 +1059,7 @@ function deserialize(s::AbstractSerializer, ::Type{Method})
meth.nargs = nargs
meth.isva = isva
meth.is_for_opaque_closure = is_for_opaque_closure
meth.nospecializeinfer = nospecializeinfer
meth.constprop = constprop
meth.purity = purity
if template !== nothing
Expand Down Expand Up @@ -1195,6 +1201,9 @@ function deserialize(s::AbstractSerializer, ::Type{CodeInfo})
if format_version(s) >= 20
ci.has_fcall = deserialize(s)
end
if format_version(s) >= 24
ci.nospecializeinfer = deserialize(s)::Bool
end
if format_version(s) >= 21
ci.inlining = deserialize(s)::UInt8
end
Expand Down
Loading

0 comments on commit 63dab32

Please sign in to comment.