Skip to content

Commit a5ab4eb

Browse files
authored
Merge branch 'master' into rationalize_irrational
2 parents 16f6a72 + 8c62f42 commit a5ab4eb

File tree

111 files changed

+3281
-1802
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

111 files changed

+3281
-1802
lines changed

Compiler/src/Compiler.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ using Core: ABIOverride, Builtin, CodeInstance, IntrinsicFunction, MethodInstanc
4949

5050
using Base
5151
using Base: @_foldable_meta, @_gc_preserve_begin, @_gc_preserve_end, @nospecializeinfer,
52-
BINDING_KIND_GLOBAL, BINDING_KIND_UNDEF_CONST, BINDING_KIND_BACKDATED_CONST,
52+
BINDING_KIND_GLOBAL, BINDING_KIND_UNDEF_CONST, BINDING_KIND_BACKDATED_CONST, BINDING_KIND_DECLARED,
5353
Base, BitVector, Bottom, Callable, DataTypeFieldDesc,
5454
EffectsOverride, Filter, Generator, IteratorSize, JLOptions, NUM_EFFECTS_OVERRIDES,
5555
OneTo, Ordering, RefValue, SizeUnknown, _NAMEDTUPLE_NAME,
@@ -72,7 +72,7 @@ using Base.Order
7272

7373
import Base: ==, _topmod, append!, convert, copy, copy!, findall, first, get, get!,
7474
getindex, haskey, in, isempty, isready, iterate, iterate, last, length, max_world,
75-
min_world, popfirst!, push!, resize!, setindex!, size
75+
min_world, popfirst!, push!, resize!, setindex!, size, intersect
7676

7777
const getproperty = Core.getfield
7878
const setproperty! = Core.setfield!

Compiler/src/abstractinterpretation.jl

Lines changed: 27 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2665,12 +2665,24 @@ function abstract_call_known(interp::AbstractInterpreter, @nospecialize(f),
26652665
end
26662666
pushfirst!(argtypes, ft)
26672667
refinements = nothing
2668-
if sv isa InferenceState && f === typeassert
2669-
# perform very limited back-propagation of invariants after this type assertion
2670-
if rt !== Bottom && isa(fargs, Vector{Any})
2668+
if sv isa InferenceState
2669+
if f === typeassert
2670+
# perform very limited back-propagation of invariants after this type assertion
2671+
if rt !== Bottom && isa(fargs, Vector{Any})
2672+
farg2 = ssa_def_slot(fargs[2], sv)
2673+
if farg2 isa SlotNumber
2674+
refinements = SlotRefinement(farg2, rt)
2675+
end
2676+
end
2677+
elseif f === setfield! && length(argtypes) == 4 && isa(argtypes[3], Const)
2678+
# from there on we know that the struct field will never be undefined,
2679+
# so we try to encode that information with a `PartialStruct`
26712680
farg2 = ssa_def_slot(fargs[2], sv)
26722681
if farg2 isa SlotNumber
2673-
refinements = SlotRefinement(farg2, rt)
2682+
refined = form_partially_defined_struct(argtypes[2], argtypes[3])
2683+
if refined !== nothing
2684+
refinements = SlotRefinement(farg2, refined)
2685+
end
26742686
end
26752687
end
26762688
end
@@ -3464,14 +3476,7 @@ world_range(ci::CodeInfo) = WorldRange(ci.min_world, ci.max_world)
34643476
world_range(ci::CodeInstance) = WorldRange(ci.min_world, ci.max_world)
34653477
world_range(compact::IncrementalCompact) = world_range(compact.ir)
34663478

3467-
function force_binding_resolution!(g::GlobalRef, world::UInt)
3468-
# Force resolution of the binding
3469-
# TODO: This will go away once we switch over to fully partitioned semantics
3470-
ccall(:jl_force_binding_resolution, Cvoid, (Any, Csize_t), g, world)
3471-
return nothing
3472-
end
3473-
3474-
function abstract_eval_globalref_type(g::GlobalRef, src::Union{CodeInfo, IRCode, IncrementalCompact}, retry_after_resolve::Bool=true)
3479+
function abstract_eval_globalref_type(g::GlobalRef, src::Union{CodeInfo, IRCode, IncrementalCompact})
34753480
worlds = world_range(src)
34763481
partition = lookup_binding_partition(min_world(worlds), g)
34773482
partition.max_world < max_world(worlds) && return Any
@@ -3480,25 +3485,18 @@ function abstract_eval_globalref_type(g::GlobalRef, src::Union{CodeInfo, IRCode,
34803485
partition = lookup_binding_partition(min_world(worlds), imported_binding)
34813486
partition.max_world < max_world(worlds) && return Any
34823487
end
3483-
if is_some_guard(binding_kind(partition))
3484-
if retry_after_resolve
3485-
# This method is surprisingly hot. For performance, don't ask the runtime to resolve
3486-
# the binding unless necessary - doing so triggers an additional lookup, which though
3487-
# not super expensive is hot enough to show up in benchmarks.
3488-
force_binding_resolution!(g, min_world(worlds))
3489-
return abstract_eval_globalref_type(g, src, false)
3490-
end
3488+
kind = binding_kind(partition)
3489+
if is_some_guard(kind)
34913490
# return Union{}
34923491
return Any
34933492
end
3494-
if is_some_const_binding(binding_kind(partition))
3493+
if is_some_const_binding(kind)
34953494
return Const(partition_restriction(partition))
34963495
end
3497-
return partition_restriction(partition)
3496+
return kind == BINDING_KIND_DECLARED ? Any : partition_restriction(partition)
34983497
end
34993498

35003499
function lookup_binding_partition!(interp::AbstractInterpreter, g::GlobalRef, sv::AbsIntState)
3501-
force_binding_resolution!(g, get_inference_world(interp))
35023500
partition = lookup_binding_partition(get_inference_world(interp), g)
35033501
update_valid_age!(sv, WorldRange(partition.min_world, partition.max_world))
35043502
partition
@@ -3541,7 +3539,11 @@ function abstract_eval_partition_load(interp::AbstractInterpreter, partition::Co
35413539
return RTEffects(rt, Union{}, Effects(EFFECTS_TOTAL, inaccessiblememonly=is_mutation_free_argtype(rt) ? ALWAYS_TRUE : ALWAYS_FALSE))
35423540
end
35433541

3544-
rt = partition_restriction(partition)
3542+
if kind == BINDING_KIND_DECLARED
3543+
rt = Any
3544+
else
3545+
rt = partition_restriction(partition)
3546+
end
35453547
return RTEffects(rt, UndefVarError, generic_getglobal_effects)
35463548
end
35473549

@@ -3580,7 +3582,7 @@ function global_assignment_binding_rt_exct(interp::AbstractInterpreter, partitio
35803582
elseif is_some_const_binding(kind)
35813583
return Pair{Any,Any}(Bottom, ErrorException)
35823584
end
3583-
ty = partition_restriction(partition)
3585+
ty = kind == BINDING_KIND_DECLARED ? Any : partition_restriction(partition)
35843586
wnewty = widenconst(newty)
35853587
if !hasintersect(wnewty, ty)
35863588
return Pair{Any,Any}(Bottom, TypeError)

Compiler/src/ssair/ir.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -581,7 +581,7 @@ function is_relevant_expr(e::Expr)
581581
:foreigncall, :isdefined, :copyast,
582582
:throw_undef_if_not,
583583
:cfunction, :method, :pop_exception,
584-
:leave,
584+
:leave, :const, :globaldecl,
585585
:new_opaque_closure)
586586
end
587587

Compiler/src/ssair/verify.jl

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,6 @@ function check_op(ir::IRCode, domtree::DomTree, @nospecialize(op), use_bb::Int,
6161
raise_error()
6262
end
6363
elseif isa(op, GlobalRef)
64-
force_binding_resolution!(op, min_world(ir.valid_worlds))
6564
bpart = lookup_binding_partition(min_world(ir.valid_worlds), op)
6665
while is_some_imported(binding_kind(bpart)) && max_world(ir.valid_worlds) <= bpart.max_world
6766
imported_binding = partition_restriction(bpart)::Core.Binding

Compiler/src/typeinfer.jl

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ If set to `true`, record per-method-instance timings within type inference in th
9292
__set_measure_typeinf(onoff::Bool) = __measure_typeinf__[] = onoff
9393
const __measure_typeinf__ = RefValue{Bool}(false)
9494

95-
function finish!(interp::AbstractInterpreter, caller::InferenceState)
95+
function finish!(interp::AbstractInterpreter, caller::InferenceState, validation_world::UInt)
9696
result = caller.result
9797
opt = result.src
9898
if opt isa OptimizationState
@@ -108,12 +108,7 @@ function finish!(interp::AbstractInterpreter, caller::InferenceState)
108108
ci = result.ci
109109
# if we aren't cached, we don't need this edge
110110
# but our caller might, so let's just make it anyways
111-
if last(result.valid_worlds) >= get_world_counter()
112-
# TODO: this should probably come after all store_backedges (after optimizations) for the entire graph in finish_cycle
113-
# since we should be requiring that all edges first get their backedges set, as a batch
114-
result.valid_worlds = WorldRange(first(result.valid_worlds), typemax(UInt))
115-
end
116-
if last(result.valid_worlds) == typemax(UInt)
111+
if last(result.valid_worlds) >= validation_world
117112
# if we can record all of the backedges in the global reverse-cache,
118113
# we can now widen our applicability in the global cache too
119114
store_backedges(ci, edges)
@@ -202,7 +197,14 @@ function finish_nocycle(::AbstractInterpreter, frame::InferenceState)
202197
if opt isa OptimizationState # implies `may_optimize(caller.interp) === true`
203198
optimize(frame.interp, opt, frame.result)
204199
end
205-
finish!(frame.interp, frame)
200+
validation_world = get_world_counter()
201+
finish!(frame.interp, frame, validation_world)
202+
if isdefined(frame.result, :ci)
203+
# After validation, under the world_counter_lock, set max_world to typemax(UInt) for all dependencies
204+
# (recursively). From that point onward the ordinary backedge mechanism is responsible for maintaining
205+
# validity.
206+
ccall(:jl_promote_ci_to_current, Cvoid, (Any, UInt), frame.result.ci, validation_world)
207+
end
206208
if frame.cycleid != 0
207209
frames = frame.callstack::Vector{AbsIntState}
208210
@assert frames[end] === frame
@@ -236,10 +238,19 @@ function finish_cycle(::AbstractInterpreter, frames::Vector{AbsIntState}, cyclei
236238
optimize(caller.interp, opt, caller.result)
237239
end
238240
end
241+
validation_world = get_world_counter()
242+
cis = CodeInstance[]
239243
for frameid = cycleid:length(frames)
240244
caller = frames[frameid]::InferenceState
241-
finish!(caller.interp, caller)
245+
finish!(caller.interp, caller, validation_world)
246+
if isdefined(caller.result, :ci)
247+
push!(cis, caller.result.ci)
248+
end
242249
end
250+
# After validation, under the world_counter_lock, set max_world to typemax(UInt) for all dependencies
251+
# (recursively). From that point onward the ordinary backedge mechanism is responsible for maintaining
252+
# validity.
253+
ccall(:jl_promote_cis_to_current, Cvoid, (Ptr{CodeInstance}, Csize_t, UInt), cis, length(cis), validation_world)
243254
resize!(frames, cycleid - 1)
244255
return nothing
245256
end
@@ -1266,6 +1277,7 @@ function typeinf_ext_toplevel(methods::Vector{Any}, worlds::Vector{UInt}, trim::
12661277
tocompile = Vector{CodeInstance}()
12671278
codeinfos = []
12681279
# first compute the ABIs of everything
1280+
latest = true # whether this_world == world_counter()
12691281
for this_world in reverse(sort!(worlds))
12701282
interp = NativeInterpreter(this_world)
12711283
for i = 1:length(methods)
@@ -1278,18 +1290,18 @@ function typeinf_ext_toplevel(methods::Vector{Any}, worlds::Vector{UInt}, trim::
12781290
# then we want to compile and emit this
12791291
if item.def.primary_world <= this_world <= item.def.deleted_world
12801292
ci = typeinf_ext(interp, item, SOURCE_MODE_NOT_REQUIRED)
1281-
ci isa CodeInstance && !use_const_api(ci) && push!(tocompile, ci)
1293+
ci isa CodeInstance && push!(tocompile, ci)
12821294
end
1283-
elseif item isa SimpleVector
1295+
elseif item isa SimpleVector && latest
12841296
(rt::Type, sig::Type) = item
12851297
# make a best-effort attempt to enqueue the relevant code for the ccallable
12861298
ptr = ccall(:jl_get_specialization1,
12871299
#= MethodInstance =# Ptr{Cvoid}, (Any, Csize_t, Cint),
12881300
sig, this_world, #= mt_cache =# 0)
12891301
if ptr !== C_NULL
1290-
mi = unsafe_pointer_to_objref(ptr)
1302+
mi = unsafe_pointer_to_objref(ptr)::MethodInstance
12911303
ci = typeinf_ext(interp, mi, SOURCE_MODE_NOT_REQUIRED)
1292-
ci isa CodeInstance && !use_const_api(ci) && push!(tocompile, ci)
1304+
ci isa CodeInstance && push!(tocompile, ci)
12931305
end
12941306
# additionally enqueue the ccallable entrypoint / adapter, which implicitly
12951307
# invokes the above ci
@@ -1305,7 +1317,7 @@ function typeinf_ext_toplevel(methods::Vector{Any}, worlds::Vector{UInt}, trim::
13051317
mi = get_ci_mi(callee)
13061318
def = mi.def
13071319
if use_const_api(callee)
1308-
src = codeinfo_for_const(interp, mi, code.rettype_const)
1320+
src = codeinfo_for_const(interp, mi, callee.rettype_const)
13091321
elseif haskey(interp.codegen, callee)
13101322
src = interp.codegen[callee]
13111323
elseif isa(def, Method) && ccall(:jl_get_module_infer, Cint, (Any,), def.module) == 0 && !trim
@@ -1327,6 +1339,7 @@ function typeinf_ext_toplevel(methods::Vector{Any}, worlds::Vector{UInt}, trim::
13271339
println("warning: failed to get code for ", mi)
13281340
end
13291341
end
1342+
latest = false
13301343
end
13311344
return codeinfos
13321345
end

Compiler/src/validation.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ const VALID_EXPR_HEADS = IdDict{Symbol,UnitRange{Int}}(
2222
:copyast => 1:1,
2323
:meta => 0:typemax(Int),
2424
:global => 1:1,
25-
:globaldecl => 2:2,
25+
:globaldecl => 1:2,
2626
:foreigncall => 5:typemax(Int), # name, RT, AT, nreq, (cconv, effects), args..., roots...
2727
:cfunction => 5:5,
2828
:isdefined => 1:2,

Compiler/test/effects.jl

Lines changed: 13 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -378,32 +378,27 @@ let effects = Base.infer_effects(; optimize=false) do
378378
end
379379

380380
# we should taint `nothrow` if the binding doesn't exist and isn't fixed yet,
381-
# as the cached effects can be easily wrong otherwise
382-
# since the inference currently doesn't track "world-age" of global variables
383-
@eval global_assignment_undefinedyet() = $(GlobalRef(@__MODULE__, :UNDEFINEDYET)) = 42
384381
setglobal!_nothrow_undefinedyet() = setglobal!(@__MODULE__, :UNDEFINEDYET, 42)
385-
let effects = Base.infer_effects() do
386-
global_assignment_undefinedyet()
387-
end
382+
let effects = Base.infer_effects(setglobal!_nothrow_undefinedyet)
388383
@test !Compiler.is_nothrow(effects)
389384
end
390-
let effects = Base.infer_effects() do
391-
setglobal!_nothrow_undefinedyet()
392-
end
393-
@test !Compiler.is_nothrow(effects)
385+
@test_throws ErrorException setglobal!_nothrow_undefinedyet()
386+
# This declares the binding as ::Any
387+
@eval global_assignment_undefinedyet() = $(GlobalRef(@__MODULE__, :UNDEFINEDYET)) = 42
388+
let effects = Base.infer_effects(global_assignment_undefinedyet)
389+
@test Compiler.is_nothrow(effects)
394390
end
395-
global UNDEFINEDYET::String = "0"
396-
let effects = Base.infer_effects() do
397-
global_assignment_undefinedyet()
398-
end
391+
# Again with type mismatch
392+
global UNDEFINEDYET2::String = "0"
393+
setglobal!_nothrow_undefinedyet2() = setglobal!(@__MODULE__, :UNDEFINEDYET2, 42)
394+
@eval global_assignment_undefinedyet2() = $(GlobalRef(@__MODULE__, :UNDEFINEDYET2)) = 42
395+
let effects = Base.infer_effects(global_assignment_undefinedyet2)
399396
@test !Compiler.is_nothrow(effects)
400397
end
401-
let effects = Base.infer_effects() do
402-
setglobal!_nothrow_undefinedyet()
403-
end
398+
let effects = Base.infer_effects(setglobal!_nothrow_undefinedyet2)
404399
@test !Compiler.is_nothrow(effects)
405400
end
406-
@test_throws Union{ErrorException,TypeError} setglobal!_nothrow_undefinedyet() # TODO: what kind of error should this be?
401+
@test_throws TypeError setglobal!_nothrow_undefinedyet2()
407402

408403
# Nothrow for setfield!
409404
mutable struct SetfieldNothrow

Compiler/test/inference.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6160,7 +6160,7 @@ end === Int
61606160
swapglobal!(@__MODULE__, :swapglobal!_xxx, x)
61616161
end === Union{}
61626162

6163-
global swapglobal!_must_throw
6163+
eval(Expr(:const, :swapglobal!_must_throw))
61646164
@newinterp SwapGlobalInterp
61656165
Compiler.InferenceParams(::SwapGlobalInterp) = Compiler.InferenceParams(; assume_bindings_static=true)
61666166
function func_swapglobal!_must_throw(x)

Compiler/test/irpasses.jl

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2042,3 +2042,14 @@ let src = code_typed1(()) do
20422042
end
20432043
@test count(iscall((src, setfield!)), src.code) == 1
20442044
end
2045+
2046+
# optimize `isdefined` away in the presence of a dominating `setfield!`
2047+
let src = code_typed1(()) do
2048+
a = Ref{Any}()
2049+
setfield!(a, :x, 2)
2050+
invokelatest(identity, a)
2051+
isdefined(a, :x) && return 1.0
2052+
a[]
2053+
end
2054+
@test count(iscall((src, isdefined)), src.code) == 0
2055+
end

0 commit comments

Comments
 (0)