Skip to content

Commit ca21e01

Browse files
authored
Merge branch 'master' into gb/sched-refact
2 parents f5d2743 + 0625b3a commit ca21e01

Some content is hidden

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

41 files changed

+404
-220
lines changed

Compiler/src/Compiler.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,8 @@ 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, Base, BitVector, Bottom, Callable, DataTypeFieldDesc,
52+
BINDING_KIND_GLOBAL, BINDING_KIND_UNDEF_CONST, BINDING_KIND_BACKDATED_CONST,
53+
Base, BitVector, Bottom, Callable, DataTypeFieldDesc,
5354
EffectsOverride, Filter, Generator, IteratorSize, JLOptions, NUM_EFFECTS_OVERRIDES,
5455
OneTo, Ordering, RefValue, SizeUnknown, _NAMEDTUPLE_NAME,
5556
_array_for, _bits_findnext, _methods_by_ftype, _uniontypes, all, allocatedinline, any,

Compiler/src/abstractinterpretation.jl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3524,6 +3524,11 @@ function abstract_eval_partition_load(interp::AbstractInterpreter, partition::Co
35243524
end
35253525

35263526
if is_defined_const_binding(kind)
3527+
if kind == BINDING_KIND_BACKDATED_CONST
3528+
# Infer this as guard. We do not want a later const definition to retroactively improve
3529+
# inference results in an earlier world.
3530+
return RTEffects(Any, UndefVarError, generic_getglobal_effects)
3531+
end
35273532
rt = Const(partition_restriction(partition))
35283533
return RTEffects(rt, Union{}, Effects(EFFECTS_TOTAL, inaccessiblememonly=is_mutation_free_argtype(rt) ? ALWAYS_TRUE : ALWAYS_FALSE))
35293534
end

Compiler/src/ssair/slot2ssa.jl

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -137,10 +137,6 @@ function fixemup!(@specialize(slot_filter), @specialize(rename_slot), ir::IRCode
137137
return nothing
138138
end
139139
op[] = x
140-
elseif isa(val, GlobalRef) && !(isdefined(val.mod, val.name) && isconst(val.mod, val.name))
141-
typ = typ_for_val(val, ci, ir, idx, Any[])
142-
new_inst = NewInstruction(val, typ)
143-
op[] = NewSSAValue(insert_node!(ir, idx, new_inst).id - length(ir.stmts))
144140
elseif isexpr(val, :static_parameter)
145141
ty = typ_for_val(val, ci, ir, idx, Any[])
146142
if isa(ty, Const)

Compiler/src/ssair/verify.jl

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ function verify_ir(ir::IRCode, print::Bool=true,
121121
if mi !== nothing
122122
push!(error_args, "\n", " Method instance: ", mi)
123123
end
124-
error(error_args...)
124+
invokelatest(error, error_args...)
125125
end
126126
# Verify CFG graph. Must be well formed to construct domtree
127127
if !(length(ir.cfg.blocks) - 1 <= length(ir.cfg.index) <= length(ir.cfg.blocks))
@@ -380,6 +380,15 @@ function verify_ir(ir::IRCode, print::Bool=true,
380380
# undefined GlobalRef is OK in isdefined
381381
continue
382382
end
383+
elseif stmt.head === :throw_undef_if_not
384+
if length(stmt.args) > 3
385+
@verify_error "malformed throw_undef_if_not"
386+
raise_error()
387+
end
388+
if stmt.args[1] isa GlobalRef
389+
# undefined GlobalRef is OK in throw_undef_if_not
390+
continue
391+
end
383392
elseif stmt.head === :gc_preserve_end
384393
# We allow gc_preserve_end tokens to span across try/catch
385394
# blocks, which isn't allowed for regular SSA values, so

Compiler/test/invalidation.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ let mi = Base.method_instance(basic_caller, (Float64,))
5555
end
5656

5757
# this redefinition below should invalidate the cache
58-
const BASIC_CALLER_WORLD = Base.get_world_counter()
58+
const BASIC_CALLER_WORLD = Base.get_world_counter()+1
5959
basic_callee(x) = x, x
6060
@test !isdefined(Base.method_instance(basic_callee, (Float64,)), :cache)
6161
let mi = Base.method_instance(basic_caller, (Float64,))

base/Base.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,8 @@ include("sysinfo.jl")
128128
include("libc.jl")
129129
using .Libc: getpid, gethostname, time, memcpy, memset, memmove, memcmp
130130

131+
const USING_STOCK_GC = occursin("stock", GC.gc_active_impl())
132+
131133
# These used to be in build_h.jl and are retained for backwards compatibility.
132134
# NOTE: keep in sync with `libblastrampoline_jll.libblastrampoline`.
133135
const libblas_name = "libblastrampoline" * (Sys.iswindows() ? "-5" : "")

base/Base_compiler.jl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,10 @@ using .Order
257257
include("coreir.jl")
258258
include("invalidation.jl")
259259

260+
# Because lowering inserts direct references, it is mandatory for this binding
261+
# to exist before we start inferring code.
262+
function string end
263+
260264
# For OS specific stuff
261265
# We need to strcat things here, before strings are really defined
262266
function strcat(x::String, y::String)

base/boot.jl

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -383,9 +383,10 @@ struct StackOverflowError <: Exception end
383383
struct UndefRefError <: Exception end
384384
struct UndefVarError <: Exception
385385
var::Symbol
386+
world::UInt
386387
scope # a Module or Symbol or other object describing the context where this variable was looked for (e.g. Main or :local or :static_parameter)
387-
UndefVarError(var::Symbol) = new(var)
388-
UndefVarError(var::Symbol, @nospecialize scope) = new(var, scope)
388+
UndefVarError(var::Symbol) = new(var, ccall(:jl_get_tls_world_age, UInt, ()))
389+
UndefVarError(var::Symbol, @nospecialize scope) = new(var, ccall(:jl_get_tls_world_age, UInt, ()), scope)
389390
end
390391
struct ConcurrencyViolationError <: Exception
391392
msg::AbstractString
@@ -717,7 +718,8 @@ macro __doc__(x)
717718
end
718719

719720
isbasicdoc(@nospecialize x) = (isa(x, Expr) && x.head === :.) || isa(x, Union{QuoteNode, Symbol})
720-
iscallexpr(ex::Expr) = (isa(ex, Expr) && ex.head === :where) ? iscallexpr(ex.args[1]) : (isa(ex, Expr) && ex.head === :call)
721+
firstarg(arg1, args...) = arg1
722+
iscallexpr(ex::Expr) = (isa(ex, Expr) && ex.head === :where) ? iscallexpr(firstarg(ex.args...)) : (isa(ex, Expr) && ex.head === :call)
721723
iscallexpr(ex) = false
722724
function ignoredoc(source, mod, str, expr)
723725
(isbasicdoc(expr) || iscallexpr(expr)) && return Expr(:escape, nothing)

base/condition.jl

Lines changed: 3 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -125,104 +125,20 @@ proceeding.
125125
"""
126126
function wait end
127127

128-
# wait with timeout
129-
#
130-
# The behavior of wait changes if a timeout is specified. There are
131-
# three concurrent entities that can interact:
132-
# 1. Task W: the task that calls wait w/timeout.
133-
# 2. Task T: the task created to handle a timeout.
134-
# 3. Task N: the task that notifies the Condition being waited on.
135-
#
136-
# Typical flow:
137-
# - W enters the Condition's wait queue.
138-
# - W creates T and stops running (calls wait()).
139-
# - T, when scheduled, waits on a Timer.
140-
# - Two common outcomes:
141-
# - N notifies the Condition.
142-
# - W starts running, closes the Timer, sets waiter_left and returns
143-
# the notify'ed value.
144-
# - The closed Timer throws an EOFError to T which simply ends.
145-
# - The Timer expires.
146-
# - T starts running and locks the Condition.
147-
# - T confirms that waiter_left is unset and that W is still in the
148-
# Condition's wait queue; it then removes W from the wait queue,
149-
# sets dosched to true and unlocks the Condition.
150-
# - If dosched is true, T schedules W with the special :timed_out
151-
# value.
152-
# - T ends.
153-
# - W runs and returns :timed_out.
154-
#
155-
# Some possible interleavings:
156-
# - N notifies the Condition but the Timer expires and T starts running
157-
# before W:
158-
# - W closing the expired Timer is benign.
159-
# - T will find that W is no longer in the Condition's wait queue
160-
# (which is protected by a lock) and will not schedule W.
161-
# - N notifies the Condition; W runs and calls wait on the Condition
162-
# again before the Timer expires:
163-
# - W sets waiter_left before leaving. When T runs, it will find that
164-
# waiter_left is set and will not schedule W.
165-
#
166-
# The lock on the Condition's wait queue and waiter_left together
167-
# ensure proper synchronization and behavior of the tasks involved.
168-
169128
"""
170-
wait(c::GenericCondition; first::Bool=false, timeout::Real=0.0)
129+
wait(c::GenericCondition; first::Bool=false)
171130
172131
Wait for [`notify`](@ref) on `c` and return the `val` parameter passed to `notify`.
173132
174133
If the keyword `first` is set to `true`, the waiter will be put _first_
175134
in line to wake up on `notify`. Otherwise, `wait` has first-in-first-out (FIFO) behavior.
176-
177-
If `timeout` is specified, cancel the `wait` when it expires and return
178-
`:timed_out`. The minimum value for `timeout` is 0.001 seconds, i.e. 1
179-
millisecond.
180135
"""
181-
function wait(c::GenericCondition; first::Bool=false, timeout::Real=0.0)
182-
timeout == 0.0 || timeout 1e-3 || throw(ArgumentError("timeout must be ≥ 1 millisecond"))
183-
136+
function wait(c::GenericCondition; first::Bool=false)
184137
ct = current_task()
185138
_wait2(c, ct, first)
186139
token = unlockall(c.lock)
187-
188-
timer::Union{Timer, Nothing} = nothing
189-
waiter_left::Union{Threads.Atomic{Bool}, Nothing} = nothing
190-
if timeout > 0.0
191-
timer = Timer(timeout)
192-
waiter_left = Threads.Atomic{Bool}(false)
193-
# start a task to wait on the timer
194-
t = Task() do
195-
try
196-
wait(timer)
197-
catch e
198-
# if the timer was closed, the waiting task has been scheduled; do nothing
199-
e isa EOFError && return
200-
end
201-
dosched = false
202-
lock(c.lock)
203-
# Confirm that the waiting task is still in the wait queue and remove it. If
204-
# the task is not in the wait queue, it must have been notified already so we
205-
# don't do anything here.
206-
if !waiter_left[] && ct.queue == c.waitq
207-
dosched = true
208-
Base.list_deletefirst!(c.waitq, ct)
209-
end
210-
unlock(c.lock)
211-
# send the waiting task a timeout
212-
dosched && schedule(ct, :timed_out)
213-
end
214-
t.sticky = false
215-
Threads._spawn_set_thrpool(t, :interactive)
216-
schedule(t)
217-
end
218-
219140
try
220-
res = wait()
221-
if timer !== nothing
222-
close(timer)
223-
waiter_left[] = true
224-
end
225-
return res
141+
return wait()
226142
catch
227143
q = ct.queue; q === nothing || Base.list_deletefirst!(q::IntrusiveLinkedList{Task}, ct)
228144
rethrow()

base/docs/bindings.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ end
1616

1717
bindingexpr(x) = Expr(:call, Binding, splitexpr(x)...)
1818

19-
defined(b::Binding) = isdefined(b.mod, b.var)
20-
resolve(b::Binding) = getfield(b.mod, b.var)
19+
defined(b::Binding) = invokelatest(isdefined, b.mod, b.var)
20+
resolve(b::Binding) = invokelatest(getfield, b.mod, b.var)
2121

2222
function splitexpr(x::Expr)
2323
isexpr(x, :macrocall) ? splitexpr(x.args[1]) :

base/experimental.jl

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
module Experimental
1111

1212
using Base: Threads, sync_varname, is_function_def, @propagate_inbounds
13+
using Base: GenericCondition
1314
using Base.Meta
1415

1516
"""
@@ -577,4 +578,112 @@ function task_wall_time_ns(t::Task=current_task())
577578
return end_at - start_at
578579
end
579580

581+
# wait_with_timeout
582+
#
583+
# A version of `wait(c::Condition)` that additionally allows the
584+
# specification of a timeout. This is experimental as it will likely
585+
# be dropped when a cancellation framework is added.
586+
#
587+
# The parallel behavior of wait_with_timeout is specified here. There
588+
# are three concurrent entities that can interact:
589+
# 1. Task W: the task that calls wait_with_timeout.
590+
# 2. Task T: the task created to handle a timeout.
591+
# 3. Task N: the task that notifies the Condition being waited on.
592+
#
593+
# Typical flow:
594+
# - W enters the Condition's wait queue.
595+
# - W creates T and stops running (calls wait()).
596+
# - T, when scheduled, waits on a Timer.
597+
# - Two common outcomes:
598+
# - N notifies the Condition.
599+
# - W starts running, closes the Timer, sets waiter_left and returns
600+
# the notify'ed value.
601+
# - The closed Timer throws an EOFError to T which simply ends.
602+
# - The Timer expires.
603+
# - T starts running and locks the Condition.
604+
# - T confirms that waiter_left is unset and that W is still in the
605+
# Condition's wait queue; it then removes W from the wait queue,
606+
# sets dosched to true and unlocks the Condition.
607+
# - If dosched is true, T schedules W with the special :timed_out
608+
# value.
609+
# - T ends.
610+
# - W runs and returns :timed_out.
611+
#
612+
# Some possible interleavings:
613+
# - N notifies the Condition but the Timer expires and T starts running
614+
# before W:
615+
# - W closing the expired Timer is benign.
616+
# - T will find that W is no longer in the Condition's wait queue
617+
# (which is protected by a lock) and will not schedule W.
618+
# - N notifies the Condition; W runs and calls wait on the Condition
619+
# again before the Timer expires:
620+
# - W sets waiter_left before leaving. When T runs, it will find that
621+
# waiter_left is set and will not schedule W.
622+
#
623+
# The lock on the Condition's wait queue and waiter_left together
624+
# ensure proper synchronization and behavior of the tasks involved.
625+
626+
"""
627+
wait_with_timeout(c::GenericCondition; first::Bool=false, timeout::Real=0.0)
628+
629+
Wait for [`notify`](@ref) on `c` and return the `val` parameter passed to `notify`.
630+
631+
If the keyword `first` is set to `true`, the waiter will be put _first_
632+
in line to wake up on `notify`. Otherwise, `wait` has first-in-first-out (FIFO) behavior.
633+
634+
If `timeout` is specified, cancel the `wait` when it expires and return
635+
`:timed_out`. The minimum value for `timeout` is 0.001 seconds, i.e. 1
636+
millisecond.
637+
"""
638+
function wait_with_timeout(c::GenericCondition; first::Bool=false, timeout::Real=0.0)
639+
ct = current_task()
640+
Base._wait2(c, ct, first)
641+
token = Base.unlockall(c.lock)
642+
643+
timer::Union{Timer, Nothing} = nothing
644+
waiter_left::Union{Threads.Atomic{Bool}, Nothing} = nothing
645+
if timeout > 0.0
646+
timer = Timer(timeout)
647+
waiter_left = Threads.Atomic{Bool}(false)
648+
# start a task to wait on the timer
649+
t = Task() do
650+
try
651+
wait(timer)
652+
catch e
653+
# if the timer was closed, the waiting task has been scheduled; do nothing
654+
e isa EOFError && return
655+
end
656+
dosched = false
657+
lock(c.lock)
658+
# Confirm that the waiting task is still in the wait queue and remove it. If
659+
# the task is not in the wait queue, it must have been notified already so we
660+
# don't do anything here.
661+
if !waiter_left[] && ct.queue == c.waitq
662+
dosched = true
663+
Base.list_deletefirst!(c.waitq, ct)
664+
end
665+
unlock(c.lock)
666+
# send the waiting task a timeout
667+
dosched && schedule(ct, :timed_out)
668+
end
669+
t.sticky = false
670+
Threads._spawn_set_thrpool(t, :interactive)
671+
schedule(t)
672+
end
673+
674+
try
675+
res = wait()
676+
if timer !== nothing
677+
close(timer)
678+
waiter_left[] = true
679+
end
680+
return res
681+
catch
682+
q = ct.queue; q === nothing || Base.list_deletefirst!(q::IntrusiveLinkedList{Task}, ct)
683+
rethrow()
684+
finally
685+
Base.relockall(c.lock, token)
686+
end
687+
end
688+
580689
end # module

base/gcutils.jl

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,4 +281,15 @@ function logging_enabled()
281281
ccall(:jl_is_gc_logging_enabled, Cint, ()) != 0
282282
end
283283

284+
"""
285+
GC.gc_active_impl()
286+
287+
Return a string stating which GC implementation is being used and possibly
288+
its version according to the list of supported GCs
289+
"""
290+
function gc_active_impl()
291+
unsafe_string(ccall(:jl_gc_active_impl, Ptr{UInt8}, ()))
292+
end
293+
294+
284295
end # module GC

base/runtime_internals.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -229,8 +229,9 @@ const BINDING_KIND_FAILED = 0x6
229229
const BINDING_KIND_DECLARED = 0x7
230230
const BINDING_KIND_GUARD = 0x8
231231
const BINDING_KIND_UNDEF_CONST = 0x9
232+
const BINDING_KIND_BACKDATED_CONST = 0xa
232233

233-
is_defined_const_binding(kind::UInt8) = (kind == BINDING_KIND_CONST || kind == BINDING_KIND_CONST_IMPORT)
234+
is_defined_const_binding(kind::UInt8) = (kind == BINDING_KIND_CONST || kind == BINDING_KIND_CONST_IMPORT || kind == BINDING_KIND_BACKDATED_CONST)
234235
is_some_const_binding(kind::UInt8) = (is_defined_const_binding(kind) || kind == BINDING_KIND_UNDEF_CONST)
235236
is_some_imported(kind::UInt8) = (kind == BINDING_KIND_IMPLICIT || kind == BINDING_KIND_EXPLICIT || kind == BINDING_KIND_IMPORTED)
236237
is_some_guard(kind::UInt8) = (kind == BINDING_KIND_GUARD || kind == BINDING_KIND_DECLARED || kind == BINDING_KIND_FAILED || kind == BINDING_KIND_UNDEF_CONST)

0 commit comments

Comments
 (0)