Skip to content

Commit f44be79

Browse files
aviateskgiordanotimholy
authored
Merge pull request #41931 from JuliaLang/avi/noinfer
* introduce `@nospecializeinfer` macro to tell the compiler to avoid excess 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). --- experiment `@nospecializeinfer` on `Core.Compiler` This commit adds `@nospecializeinfer` macro on various `Core.Compiler` functions and achieves the following sysimage size reduction: | | this commit | master | % | | --------------------------------- | ----------- | ----------- | ------- | | `Core.Compiler` compilation (sec) | `66.4551` | `71.0846` | `0.935` | | `corecompiler.jl` (KB) | `17638080` | `18407248` | `0.958` | | `sys.jl` (KB) | `88736432` | `89361280` | `0.993` | | `sys-o.a` (KB) | `189484400` | `189907096` | `0.998` | --------- Co-authored-by: MosΓ¨ Giordano <[email protected]> Co-authored-by: Tim Holy <[email protected]>
2 parents 944b28c + 1dc2ed6 commit f44be79

File tree

18 files changed

+302
-118
lines changed

18 files changed

+302
-118
lines changed

β€Žbase/compiler/abstractinterpretation.jl

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -508,6 +508,10 @@ function abstract_call_method(interp::AbstractInterpreter,
508508
sigtuple = unwrap_unionall(sig)
509509
sigtuple isa DataType || return MethodCallResult(Any, false, false, nothing, Effects())
510510

511+
if is_nospecializeinfer(method)
512+
sig = get_nospecializeinfer_sig(method, sig, sparams)
513+
end
514+
511515
# Limit argument type tuple growth of functions:
512516
# look through the parents list to see if there's a call to the same method
513517
# and from the same method.
@@ -2645,18 +2649,18 @@ struct BestguessInfo{Interp<:AbstractInterpreter}
26452649
end
26462650
end
26472651

2648-
function widenreturn(@nospecialize(rt), info::BestguessInfo)
2652+
@nospecializeinfer function widenreturn(@nospecialize(rt), info::BestguessInfo)
26492653
return widenreturn(typeinf_lattice(info.interp), rt, info)
26502654
end
26512655

2652-
function widenreturn(𝕃ᡒ::AbstractLattice, @nospecialize(rt), info::BestguessInfo)
2656+
@nospecializeinfer function widenreturn(𝕃ᡒ::AbstractLattice, @nospecialize(rt), info::BestguessInfo)
26532657
return widenreturn(widenlattice(𝕃ᡒ), rt, info)
26542658
end
2655-
function widenreturn_noslotwrapper(𝕃ᡒ::AbstractLattice, @nospecialize(rt), info::BestguessInfo)
2659+
@nospecializeinfer function widenreturn_noslotwrapper(𝕃ᡒ::AbstractLattice, @nospecialize(rt), info::BestguessInfo)
26562660
return widenreturn_noslotwrapper(widenlattice(𝕃ᡒ), rt, info)
26572661
end
26582662

2659-
function widenreturn(𝕃ᡒ::MustAliasesLattice, @nospecialize(rt), info::BestguessInfo)
2663+
@nospecializeinfer function widenreturn(𝕃ᡒ::MustAliasesLattice, @nospecialize(rt), info::BestguessInfo)
26602664
if isa(rt, MustAlias)
26612665
if 1 ≀ rt.slot ≀ info.nargs
26622666
rt = InterMustAlias(rt)
@@ -2668,7 +2672,7 @@ function widenreturn(𝕃ᡒ::MustAliasesLattice, @nospecialize(rt), info::Bestg
26682672
return widenreturn(widenlattice(𝕃ᡒ), rt, info)
26692673
end
26702674

2671-
function widenreturn(𝕃ᡒ::ConditionalsLattice, @nospecialize(rt), info::BestguessInfo)
2675+
@nospecializeinfer function widenreturn(𝕃ᡒ::ConditionalsLattice, @nospecialize(rt), info::BestguessInfo)
26722676
βŠ‘α΅’ = βŠ‘(𝕃ᡒ)
26732677
if !(βŠ‘(ipo_lattice(info.interp), info.bestguess, Bool)) || info.bestguess === Bool
26742678
# give up inter-procedural constraint back-propagation
@@ -2705,7 +2709,7 @@ function widenreturn(𝕃ᡒ::ConditionalsLattice, @nospecialize(rt), info::Best
27052709
isa(rt, InterConditional) && return rt
27062710
return widenreturn(widenlattice(𝕃ᡒ), rt, info)
27072711
end
2708-
function bool_rt_to_conditional(@nospecialize(rt), info::BestguessInfo)
2712+
@nospecializeinfer function bool_rt_to_conditional(@nospecialize(rt), info::BestguessInfo)
27092713
bestguess = info.bestguess
27102714
if isa(bestguess, InterConditional)
27112715
# if the bestguess so far is already `Conditional`, try to convert
@@ -2723,7 +2727,7 @@ function bool_rt_to_conditional(@nospecialize(rt), info::BestguessInfo)
27232727
end
27242728
return rt
27252729
end
2726-
function bool_rt_to_conditional(@nospecialize(rt), slot_id::Int, info::BestguessInfo)
2730+
@nospecializeinfer function bool_rt_to_conditional(@nospecialize(rt), slot_id::Int, info::BestguessInfo)
27272731
βŠ‘α΅’ = βŠ‘(typeinf_lattice(info.interp))
27282732
old = info.slottypes[slot_id]
27292733
new = widenslotwrapper(info.changes[slot_id].typ) # avoid nested conditional
@@ -2742,13 +2746,13 @@ function bool_rt_to_conditional(@nospecialize(rt), slot_id::Int, info::Bestguess
27422746
return rt
27432747
end
27442748

2745-
function widenreturn(𝕃ᡒ::PartialsLattice, @nospecialize(rt), info::BestguessInfo)
2749+
@nospecializeinfer function widenreturn(𝕃ᡒ::PartialsLattice, @nospecialize(rt), info::BestguessInfo)
27462750
return widenreturn_partials(𝕃ᡒ, rt, info)
27472751
end
2748-
function widenreturn_noslotwrapper(𝕃ᡒ::PartialsLattice, @nospecialize(rt), info::BestguessInfo)
2752+
@nospecializeinfer function widenreturn_noslotwrapper(𝕃ᡒ::PartialsLattice, @nospecialize(rt), info::BestguessInfo)
27492753
return widenreturn_partials(𝕃ᡒ, rt, info)
27502754
end
2751-
function widenreturn_partials(𝕃ᡒ::PartialsLattice, @nospecialize(rt), info::BestguessInfo)
2755+
@nospecializeinfer function widenreturn_partials(𝕃ᡒ::PartialsLattice, @nospecialize(rt), info::BestguessInfo)
27522756
if isa(rt, PartialStruct)
27532757
fields = copy(rt.fields)
27542758
local anyrefine = false
@@ -2771,21 +2775,21 @@ function widenreturn_partials(𝕃ᡒ::PartialsLattice, @nospecialize(rt), info:
27712775
return widenreturn(widenlattice(𝕃ᡒ), rt, info)
27722776
end
27732777

2774-
function widenreturn(::ConstsLattice, @nospecialize(rt), ::BestguessInfo)
2778+
@nospecializeinfer function widenreturn(::ConstsLattice, @nospecialize(rt), ::BestguessInfo)
27752779
return widenreturn_consts(rt)
27762780
end
2777-
function widenreturn_noslotwrapper(::ConstsLattice, @nospecialize(rt), ::BestguessInfo)
2781+
@nospecializeinfer function widenreturn_noslotwrapper(::ConstsLattice, @nospecialize(rt), ::BestguessInfo)
27782782
return widenreturn_consts(rt)
27792783
end
2780-
function widenreturn_consts(@nospecialize(rt))
2784+
@nospecializeinfer function widenreturn_consts(@nospecialize(rt))
27812785
isa(rt, Const) && return rt
27822786
return widenconst(rt)
27832787
end
27842788

2785-
function widenreturn(::JLTypeLattice, @nospecialize(rt), ::BestguessInfo)
2789+
@nospecializeinfer function widenreturn(::JLTypeLattice, @nospecialize(rt), ::BestguessInfo)
27862790
return widenconst(rt)
27872791
end
2788-
function widenreturn_noslotwrapper(::JLTypeLattice, @nospecialize(rt), ::BestguessInfo)
2792+
@nospecializeinfer function widenreturn_noslotwrapper(::JLTypeLattice, @nospecialize(rt), ::BestguessInfo)
27892793
return widenconst(rt)
27902794
end
27912795

β€Žbase/compiler/abstractlattice.jl

Lines changed: 25 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -161,23 +161,23 @@ If `𝕃` is `JLTypeLattice`, this is equivalent to subtyping.
161161
"""
162162
function βŠ‘ end
163163

164-
βŠ‘(::JLTypeLattice, @nospecialize(a::Type), @nospecialize(b::Type)) = a <: b
164+
@nospecializeinfer βŠ‘(::JLTypeLattice, @nospecialize(a::Type), @nospecialize(b::Type)) = a <: b
165165

166166
"""
167167
⊏(𝕃::AbstractLattice, a, b) -> Bool
168168
169169
The strict partial order over the type inference lattice.
170170
This is defined as the irreflexive kernel of `βŠ‘`.
171171
"""
172-
⊏(𝕃::AbstractLattice, @nospecialize(a), @nospecialize(b)) = βŠ‘(𝕃, a, b) && !βŠ‘(𝕃, b, a)
172+
@nospecializeinfer ⊏(𝕃::AbstractLattice, @nospecialize(a), @nospecialize(b)) = βŠ‘(𝕃, a, b) && !βŠ‘(𝕃, b, a)
173173

174174
"""
175175
β‹€(𝕃::AbstractLattice, a, b) -> Bool
176176
177177
This order could be used as a slightly more efficient version of the strict order `⊏`,
178178
where we can safely assume `a βŠ‘ b` holds.
179179
"""
180-
β‹€(𝕃::AbstractLattice, @nospecialize(a), @nospecialize(b)) = !βŠ‘(𝕃, b, a)
180+
@nospecializeinfer β‹€(𝕃::AbstractLattice, @nospecialize(a), @nospecialize(b)) = !βŠ‘(𝕃, b, a)
181181

182182
"""
183183
is_lattice_equal(𝕃::AbstractLattice, a, b) -> Bool
@@ -186,7 +186,7 @@ Check if two lattice elements are partial order equivalent.
186186
This is basically `a βŠ‘ b && b βŠ‘ a` in the lattice of `𝕃`
187187
but (optionally) with extra performance optimizations.
188188
"""
189-
function is_lattice_equal(𝕃::AbstractLattice, @nospecialize(a), @nospecialize(b))
189+
@nospecializeinfer function is_lattice_equal(𝕃::AbstractLattice, @nospecialize(a), @nospecialize(b))
190190
a === b && return true
191191
return βŠ‘(𝕃, a, b) && βŠ‘(𝕃, b, a)
192192
end
@@ -197,32 +197,32 @@ end
197197
Determines whether the given lattice element `t` of `𝕃` has non-trivial extended lattice
198198
information that would not be available from the type itself.
199199
"""
200-
has_nontrivial_extended_info(𝕃::AbstractLattice, @nospecialize t) =
200+
@nospecializeinfer has_nontrivial_extended_info(𝕃::AbstractLattice, @nospecialize t) =
201201
has_nontrivial_extended_info(widenlattice(𝕃), t)
202-
function has_nontrivial_extended_info(𝕃::PartialsLattice, @nospecialize t)
202+
@nospecializeinfer function has_nontrivial_extended_info(𝕃::PartialsLattice, @nospecialize t)
203203
isa(t, PartialStruct) && return true
204204
isa(t, PartialOpaque) && return true
205205
return has_nontrivial_extended_info(widenlattice(𝕃), t)
206206
end
207-
function has_nontrivial_extended_info(𝕃::ConstsLattice, @nospecialize t)
207+
@nospecializeinfer function has_nontrivial_extended_info(𝕃::ConstsLattice, @nospecialize t)
208208
isa(t, PartialTypeVar) && return true
209209
if isa(t, Const)
210210
val = t.val
211211
return !issingletontype(typeof(val)) && !(isa(val, Type) && hasuniquerep(val))
212212
end
213213
return has_nontrivial_extended_info(widenlattice(𝕃), t)
214214
end
215-
has_nontrivial_extended_info(::JLTypeLattice, @nospecialize(t)) = false
215+
@nospecializeinfer has_nontrivial_extended_info(::JLTypeLattice, @nospecialize(t)) = false
216216

217217
"""
218218
is_const_prop_profitable_arg(𝕃::AbstractLattice, t) -> Bool
219219
220220
Determines whether the given lattice element `t` of `𝕃` has new extended lattice information
221221
that should be forwarded along with constant propagation.
222222
"""
223-
is_const_prop_profitable_arg(𝕃::AbstractLattice, @nospecialize t) =
223+
@nospecializeinfer is_const_prop_profitable_arg(𝕃::AbstractLattice, @nospecialize t) =
224224
is_const_prop_profitable_arg(widenlattice(𝕃), t)
225-
function is_const_prop_profitable_arg(𝕃::PartialsLattice, @nospecialize t)
225+
@nospecializeinfer function is_const_prop_profitable_arg(𝕃::PartialsLattice, @nospecialize t)
226226
if isa(t, PartialStruct)
227227
return true # might be a bit aggressive, may want to enable some check like follows:
228228
# for i = 1:length(t.fields)
@@ -236,7 +236,7 @@ function is_const_prop_profitable_arg(𝕃::PartialsLattice, @nospecialize t)
236236
isa(t, PartialOpaque) && return true
237237
return is_const_prop_profitable_arg(widenlattice(𝕃), t)
238238
end
239-
function is_const_prop_profitable_arg(𝕃::ConstsLattice, @nospecialize t)
239+
@nospecializeinfer function is_const_prop_profitable_arg(𝕃::ConstsLattice, @nospecialize t)
240240
if isa(t, Const)
241241
# don't consider mutable values useful constants
242242
val = t.val
@@ -245,24 +245,24 @@ function is_const_prop_profitable_arg(𝕃::ConstsLattice, @nospecialize t)
245245
isa(t, PartialTypeVar) && return false # this isn't forwardable
246246
return is_const_prop_profitable_arg(widenlattice(𝕃), t)
247247
end
248-
is_const_prop_profitable_arg(::JLTypeLattice, @nospecialize t) = false
248+
@nospecializeinfer is_const_prop_profitable_arg(::JLTypeLattice, @nospecialize t) = false
249249

250-
is_forwardable_argtype(𝕃::AbstractLattice, @nospecialize(x)) =
250+
@nospecializeinfer is_forwardable_argtype(𝕃::AbstractLattice, @nospecialize(x)) =
251251
is_forwardable_argtype(widenlattice(𝕃), x)
252-
function is_forwardable_argtype(𝕃::ConditionalsLattice, @nospecialize x)
252+
@nospecializeinfer function is_forwardable_argtype(𝕃::ConditionalsLattice, @nospecialize x)
253253
isa(x, Conditional) && return true
254254
return is_forwardable_argtype(widenlattice(𝕃), x)
255255
end
256-
function is_forwardable_argtype(𝕃::PartialsLattice, @nospecialize x)
256+
@nospecializeinfer function is_forwardable_argtype(𝕃::PartialsLattice, @nospecialize x)
257257
isa(x, PartialStruct) && return true
258258
isa(x, PartialOpaque) && return true
259259
return is_forwardable_argtype(widenlattice(𝕃), x)
260260
end
261-
function is_forwardable_argtype(𝕃::ConstsLattice, @nospecialize x)
261+
@nospecializeinfer function is_forwardable_argtype(𝕃::ConstsLattice, @nospecialize x)
262262
isa(x, Const) && return true
263263
return is_forwardable_argtype(widenlattice(𝕃), x)
264264
end
265-
function is_forwardable_argtype(::JLTypeLattice, @nospecialize x)
265+
@nospecializeinfer function is_forwardable_argtype(::JLTypeLattice, @nospecialize x)
266266
return false
267267
end
268268

@@ -281,9 +281,9 @@ External lattice `𝕃ᡒ::ExternalLattice` may overload:
281281
"""
282282
function widenreturn end, function widenreturn_noslotwrapper end
283283

284-
is_valid_lattice(𝕃::AbstractLattice, @nospecialize(elem)) =
284+
@nospecializeinfer is_valid_lattice(𝕃::AbstractLattice, @nospecialize(elem)) =
285285
is_valid_lattice_norec(𝕃, elem) && is_valid_lattice(widenlattice(𝕃), elem)
286-
is_valid_lattice(𝕃::JLTypeLattice, @nospecialize(elem)) = is_valid_lattice_norec(𝕃, elem)
286+
@nospecializeinfer is_valid_lattice(𝕃::JLTypeLattice, @nospecialize(elem)) = is_valid_lattice_norec(𝕃, elem)
287287

288288
has_conditional(𝕃::AbstractLattice) = has_conditional(widenlattice(𝕃))
289289
has_conditional(::AnyConditionalsLattice) = true
@@ -306,12 +306,12 @@ has_extended_unionsplit(::JLTypeLattice) = false
306306
const fallback_lattice = InferenceLattice(BaseInferenceLattice.instance)
307307
const fallback_ipo_lattice = InferenceLattice(IPOResultLattice.instance)
308308

309-
βŠ‘(@nospecialize(a), @nospecialize(b)) = βŠ‘(fallback_lattice, a, b)
310-
tmeet(@nospecialize(a), @nospecialize(b)) = tmeet(fallback_lattice, a, b)
311-
tmerge(@nospecialize(a), @nospecialize(b)) = tmerge(fallback_lattice, a, b)
312-
⊏(@nospecialize(a), @nospecialize(b)) = ⊏(fallback_lattice, a, b)
313-
β‹€(@nospecialize(a), @nospecialize(b)) = β‹€(fallback_lattice, a, b)
314-
is_lattice_equal(@nospecialize(a), @nospecialize(b)) = is_lattice_equal(fallback_lattice, a, b)
309+
@nospecializeinfer @nospecialize(a) βŠ‘ @nospecialize(b) = βŠ‘(fallback_lattice, a, b)
310+
@nospecializeinfer @nospecialize(a) ⊏ @nospecialize(b) = ⊏(fallback_lattice, a, b)
311+
@nospecializeinfer @nospecialize(a) β‹€ @nospecialize(b) = β‹€(fallback_lattice, a, b)
312+
@nospecializeinfer tmeet(@nospecialize(a), @nospecialize(b)) = tmeet(fallback_lattice, a, b)
313+
@nospecializeinfer tmerge(@nospecialize(a), @nospecialize(b)) = tmerge(fallback_lattice, a, b)
314+
@nospecializeinfer is_lattice_equal(@nospecialize(a), @nospecialize(b)) = is_lattice_equal(fallback_lattice, a, b)
315315

316316
# Widenlattice with argument
317317
widenlattice(::JLTypeLattice, @nospecialize(t)) = widenconst(t)

0 commit comments

Comments
Β (0)