Skip to content

Commit

Permalink
Merge branch 'master' into eph/limit-repl
Browse files Browse the repository at this point in the history
  • Loading branch information
ericphanson authored Jul 24, 2024
2 parents 0bb1298 + e478e12 commit 1add163
Show file tree
Hide file tree
Showing 148 changed files with 2,255 additions and 1,498 deletions.
6 changes: 2 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,8 @@ New developers may find the notes in
[CONTRIBUTING](https://github.com/JuliaLang/julia/blob/master/CONTRIBUTING.md)
helpful to start contributing to the Julia codebase.

### External Resources
### Learning Julia

- [**StackOverflow**](https://stackoverflow.com/questions/tagged/julia-lang)
- [**Twitter**](https://twitter.com/JuliaLanguage)
- [**Learning resources**](https://julialang.org/learning/)

## Binary Installation
Expand Down Expand Up @@ -94,7 +92,7 @@ and then use the command prompt to change into the resulting julia directory. By
Julia. However, most users should use the [most recent stable version](https://github.com/JuliaLang/julia/releases)
of Julia. You can get this version by running:

git checkout v1.10.3
git checkout v1.10.4

To build the `julia` executable, run `make` from within the julia directory.

Expand Down
36 changes: 20 additions & 16 deletions base/Base.jl
Original file line number Diff line number Diff line change
Expand Up @@ -614,6 +614,25 @@ function profile_printing_listener(cond::Base.AsyncCondition)
nothing
end

function start_profile_listener()
cond = Base.AsyncCondition()
Base.uv_unref(cond.handle)
t = errormonitor(Threads.@spawn(profile_printing_listener(cond)))
atexit() do
# destroy this callback when exiting
ccall(:jl_set_peek_cond, Cvoid, (Ptr{Cvoid},), C_NULL)
# this will prompt any ongoing or pending event to flush also
close(cond)
# error-propagation is not needed, since the errormonitor will handle printing that better
_wait(t)
end
finalizer(cond) do c
# if something goes south, still make sure we aren't keeping a reference in C to this
ccall(:jl_set_peek_cond, Cvoid, (Ptr{Cvoid},), C_NULL)
end
ccall(:jl_set_peek_cond, Cvoid, (Ptr{Cvoid},), cond.handle)
end

function __init__()
# Base library init
global _atexit_hooks_finished = false
Expand All @@ -636,22 +655,7 @@ function __init__()
# Profiling helper
@static if !Sys.iswindows()
# triggering a profile via signals is not implemented on windows
cond = Base.AsyncCondition()
Base.uv_unref(cond.handle)
t = errormonitor(Threads.@spawn(profile_printing_listener(cond)))
atexit() do
# destroy this callback when exiting
ccall(:jl_set_peek_cond, Cvoid, (Ptr{Cvoid},), C_NULL)
# this will prompt any ongoing or pending event to flush also
close(cond)
# error-propagation is not needed, since the errormonitor will handle printing that better
_wait(t)
end
finalizer(cond) do c
# if something goes south, still make sure we aren't keeping a reference in C to this
ccall(:jl_set_peek_cond, Cvoid, (Ptr{Cvoid},), C_NULL)
end
ccall(:jl_set_peek_cond, Cvoid, (Ptr{Cvoid},), cond.handle)
start_profile_listener()
end
_require_world_age[] = get_world_counter()
# Prevent spawned Julia process from getting stuck waiting on Tracy to connect.
Expand Down
8 changes: 4 additions & 4 deletions base/abstractarray.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1659,10 +1659,10 @@ typed_vcat(::Type{T}) where {T} = Vector{T}()
typed_hcat(::Type{T}) where {T} = Vector{T}()

## cat: special cases
vcat(X::T...) where {T} = T[ X[i] for i=1:length(X) ]
vcat(X::T...) where {T<:Number} = T[ X[i] for i=1:length(X) ]
hcat(X::T...) where {T} = T[ X[j] for i=1:1, j=1:length(X) ]
hcat(X::T...) where {T<:Number} = T[ X[j] for i=1:1, j=1:length(X) ]
vcat(X::T...) where {T} = T[ X[i] for i=eachindex(X) ]
vcat(X::T...) where {T<:Number} = T[ X[i] for i=eachindex(X) ]
hcat(X::T...) where {T} = T[ X[j] for i=1:1, j=eachindex(X) ]
hcat(X::T...) where {T<:Number} = T[ X[j] for i=1:1, j=eachindex(X) ]

vcat(X::Number...) = hvcat_fill!(Vector{promote_typeof(X...)}(undef, length(X)), X)
hcat(X::Number...) = hvcat_fill!(Matrix{promote_typeof(X...)}(undef, 1,length(X)), X)
Expand Down
29 changes: 18 additions & 11 deletions base/asyncevent.jl
Original file line number Diff line number Diff line change
Expand Up @@ -127,8 +127,11 @@ function _trywait(t::Union{Timer, AsyncCondition})
t isa Timer || Core.Intrinsics.atomic_fence(:acquire_release)
else
if !isopen(t)
close(t) # wait for the close to complete
return false
set = t.set
if !set
close(t) # wait for the close to complete
return false
end
end
iolock_begin()
set = t.set
Expand All @@ -151,7 +154,7 @@ function _trywait(t::Union{Timer, AsyncCondition})
end
iolock_end()
end
@atomic :monotonic t.set = false
@atomic :monotonic t.set = false # if there are multiple waiters, an unspecified number may short-circuit past here
return set
end

Expand All @@ -161,14 +164,14 @@ function wait(t::Union{Timer, AsyncCondition})
end


isopen(t::Union{Timer, AsyncCondition}) = t.isopen && t.handle != C_NULL
isopen(t::Union{Timer, AsyncCondition}) = @atomic :acquire t.isopen

function close(t::Union{Timer, AsyncCondition})
t.handle == C_NULL && return # short-circuit path
t.handle == C_NULL && !t.isopen && return # short-circuit path, :monotonic
iolock_begin()
if t.handle != C_NULL
if t.isopen
@atomic :monotonic t.isopen = false
@atomic :release t.isopen = false
ccall(:jl_close_uv, Cvoid, (Ptr{Cvoid},), t)
end
# implement _trywait here without the auto-reset function, just waiting for the final close signal
Expand All @@ -186,6 +189,8 @@ function close(t::Union{Timer, AsyncCondition})
unlock(t.cond)
unpreserve_handle(t)
end
elseif t.isopen
@atomic :release t.isopen = false
end
iolock_end()
nothing
Expand All @@ -198,8 +203,8 @@ function uvfinalize(t::Union{Timer, AsyncCondition})
if t.handle != C_NULL
disassociate_julia_struct(t.handle) # not going to call the usual close hooks anymore
if t.isopen
@atomic :monotonic t.isopen = false
ccall(:jl_close_uv, Cvoid, (Ptr{Cvoid},), t.handle)
@atomic :release t.isopen = false
ccall(:jl_close_uv, Cvoid, (Ptr{Cvoid},), t.handle) # this will call Libc.free
end
@atomic :monotonic t.handle = C_NULL
notify(t.cond, false)
Expand All @@ -214,8 +219,10 @@ end
function _uv_hook_close(t::Union{Timer, AsyncCondition})
lock(t.cond)
try
@atomic :monotonic t.isopen = false
Libc.free(@atomicswap :monotonic t.handle = C_NULL)
handle = t.handle
@atomic :release t.isopen = false
@atomic :monotonic t.handle = C_NULL
Libc.free(handle)
notify(t.cond, false)
finally
unlock(t.cond)
Expand Down Expand Up @@ -243,7 +250,7 @@ function uv_timercb(handle::Ptr{Cvoid})
if ccall(:uv_timer_get_repeat, UInt64, (Ptr{Cvoid},), t) == 0
# timer is stopped now
if t.isopen
@atomic :monotonic t.isopen = false
@atomic :release t.isopen = false
ccall(:jl_close_uv, Cvoid, (Ptr{Cvoid},), t)
end
end
Expand Down
4 changes: 2 additions & 2 deletions base/client.jl
Original file line number Diff line number Diff line change
Expand Up @@ -620,11 +620,11 @@ const main = MyApp.main
"""
macro main(args...)
if !isempty(args)
error("USAGE: `@main` is expected to be used as `(@main)` without macro arguments.")
error("`@main` is expected to be used as `(@main)` without macro arguments.")
end
if isdefined(__module__, :main)
if Base.binding_module(__module__, :main) !== __module__
error("USAGE: Symbol `main` is already a resolved import in module $(__module__). `@main` must be used in the defining module.")
error("Symbol `main` is already a resolved import in module $(__module__). `@main` must be used in the defining module.")
end
end
Core.eval(__module__, quote
Expand Down
62 changes: 42 additions & 20 deletions base/compiler/abstractinterpretation.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2705,6 +2705,8 @@ function abstract_eval_statement_expr(interp::AbstractInterpreter, e::Expr, vtyp
elseif ehead === :gc_preserve_end || ehead === :leave || ehead === :pop_exception ||
ehead === :global || ehead === :popaliasscope
return RTEffects(Nothing, Union{}, Effects(EFFECTS_TOTAL; effect_free=EFFECT_FREE_GLOBALLY))
elseif ehead === :globaldecl
return RTEffects(Nothing, Any, EFFECTS_UNKNOWN)
elseif ehead === :thunk
return RTEffects(Any, Any, EFFECTS_UNKNOWN)
end
Expand Down Expand Up @@ -3065,7 +3067,7 @@ end
@inline function abstract_eval_basic_statement(interp::AbstractInterpreter,
@nospecialize(stmt), pc_vartable::VarTable, frame::InferenceState)
if isa(stmt, NewvarNode)
changes = StateUpdate(stmt.slot, VarState(Bottom, true), pc_vartable, false)
changes = StateUpdate(stmt.slot, VarState(Bottom, true), false)
return BasicStmtChange(changes, nothing, Union{})
elseif !isa(stmt, Expr)
(; rt, exct) = abstract_eval_statement(interp, stmt, pc_vartable, frame)
Expand All @@ -3080,7 +3082,7 @@ end
end
lhs = stmt.args[1]
if isa(lhs, SlotNumber)
changes = StateUpdate(lhs, VarState(rt, false), pc_vartable, false)
changes = StateUpdate(lhs, VarState(rt, false), false)
elseif isa(lhs, GlobalRef)
handle_global_assignment!(interp, frame, lhs, rt)
elseif !isa(lhs, SSAValue)
Expand All @@ -3090,7 +3092,7 @@ end
elseif hd === :method
fname = stmt.args[1]
if isa(fname, SlotNumber)
changes = StateUpdate(fname, VarState(Any, false), pc_vartable, false)
changes = StateUpdate(fname, VarState(Any, false), false)
end
return BasicStmtChange(changes, nothing, Union{})
elseif (hd === :code_coverage_effect || (
Expand Down Expand Up @@ -3240,18 +3242,18 @@ function typeinf_local(interp::AbstractInterpreter, frame::InferenceState)
@goto branch
elseif isa(stmt, GotoIfNot)
condx = stmt.cond
condxslot = ssa_def_slot(condx, frame)
condslot = ssa_def_slot(condx, frame)
condt = abstract_eval_value(interp, condx, currstate, frame)
if condt === Bottom
ssavaluetypes[currpc] = Bottom
empty!(frame.pclimitations)
@goto find_next_bb
end
orig_condt = condt
if !(isa(condt, Const) || isa(condt, Conditional)) && isa(condxslot, SlotNumber)
if !(isa(condt, Const) || isa(condt, Conditional)) && isa(condslot, SlotNumber)
# if this non-`Conditional` object is a slot, we form and propagate
# the conditional constraint on it
condt = Conditional(condxslot, Const(true), Const(false))
condt = Conditional(condslot, Const(true), Const(false))
end
condval = maybe_extract_const_bool(condt)
nothrow = (condval !== nothing) || (𝕃ᵢ, orig_condt, Bool)
Expand Down Expand Up @@ -3297,21 +3299,31 @@ function typeinf_local(interp::AbstractInterpreter, frame::InferenceState)
# We continue with the true branch, but process the false
# branch here.
if isa(condt, Conditional)
else_change = conditional_change(𝕃ᵢ, currstate, condt.elsetype, condt.slot)
else_change = conditional_change(𝕃ᵢ, currstate, condt, #=then_or_else=#false)
if else_change !== nothing
false_vartable = stoverwrite1!(copy(currstate), else_change)
elsestate = copy(currstate)
stoverwrite1!(elsestate, else_change)
elseif condslot isa SlotNumber
elsestate = copy(currstate)
else
false_vartable = currstate
elsestate = currstate
end
changed = update_bbstate!(𝕃ᵢ, frame, falsebb, false_vartable)
then_change = conditional_change(𝕃ᵢ, currstate, condt.thentype, condt.slot)
if condslot isa SlotNumber # refine the type of this conditional object itself for this else branch
stoverwrite1!(elsestate, condition_object_change(currstate, condt, condslot, #=then_or_else=#false))
end
else_changed = update_bbstate!(𝕃ᵢ, frame, falsebb, elsestate)
then_change = conditional_change(𝕃ᵢ, currstate, condt, #=then_or_else=#true)
thenstate = currstate
if then_change !== nothing
stoverwrite1!(currstate, then_change)
stoverwrite1!(thenstate, then_change)
end
if condslot isa SlotNumber # refine the type of this conditional object itself for this then branch
stoverwrite1!(thenstate, condition_object_change(currstate, condt, condslot, #=then_or_else=#true))
end
else
changed = update_bbstate!(𝕃ᵢ, frame, falsebb, currstate)
else_changed = update_bbstate!(𝕃ᵢ, frame, falsebb, currstate)
end
if changed
if else_changed
handle_control_backedge!(interp, frame, currpc, stmt.dest)
push!(W, falsebb)
end
Expand Down Expand Up @@ -3410,13 +3422,14 @@ function typeinf_local(interp::AbstractInterpreter, frame::InferenceState)
nothing
end

function conditional_change(𝕃ᵢ::AbstractLattice, state::VarTable, @nospecialize(typ), slot::Int)
vtype = state[slot]
function conditional_change(𝕃ᵢ::AbstractLattice, currstate::VarTable, condt::Conditional, then_or_else::Bool)
vtype = currstate[condt.slot]
oldtyp = vtype.typ
if iskindtype(typ)
newtyp = then_or_else ? condt.thentype : condt.elsetype
if iskindtype(newtyp)
# this code path corresponds to the special handling for `isa(x, iskindtype)` check
# implemented within `abstract_call_builtin`
elseif (𝕃ᵢ, ignorelimited(typ), ignorelimited(oldtyp))
elseif (𝕃ᵢ, ignorelimited(newtyp), ignorelimited(oldtyp))
# approximate test for `typ ∩ oldtyp` being better than `oldtyp`
# since we probably formed these types with `typesubstract`,
# the comparison is likely simple
Expand All @@ -3426,9 +3439,18 @@ function conditional_change(𝕃ᵢ::AbstractLattice, state::VarTable, @nospecia
if oldtyp isa LimitedAccuracy
# typ is better unlimited, but we may still need to compute the tmeet with the limit
# "causes" since we ignored those in the comparison
typ = tmerge(𝕃ᵢ, typ, LimitedAccuracy(Bottom, oldtyp.causes))
newtyp = tmerge(𝕃ᵢ, newtyp, LimitedAccuracy(Bottom, oldtyp.causes))
end
return StateUpdate(SlotNumber(slot), VarState(typ, vtype.undef), state, true)
return StateUpdate(SlotNumber(condt.slot), VarState(newtyp, vtype.undef), true)
end

function condition_object_change(currstate::VarTable, condt::Conditional,
condslot::SlotNumber, then_or_else::Bool)
vtype = currstate[slot_id(condslot)]
newcondt = Conditional(condt.slot,
then_or_else ? condt.thentype : Union{},
then_or_else ? Union{} : condt.elsetype)
return StateUpdate(condslot, VarState(newcondt, vtype.undef), false)
end

# make as much progress on `frame` as possible (by handling cycles)
Expand Down
16 changes: 8 additions & 8 deletions base/compiler/optimize.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1178,20 +1178,19 @@ function convert_to_ircode(ci::CodeInfo, sv::OptimizationState)
# Any statements from here to the end of the block have been wrapped in Core.Const(...)
# by type inference (effectively deleting them). Only task left is to replace the block
# terminator with an explicit `unreachable` marker.
if block_end > idx
code[block_end] = ReturnNode()
codelocs[3block_end-2], codelocs[3block_end-1], codelocs[3block_end-0] = (codelocs[3idx-2], codelocs[3idx-1], codelocs[3idx-0])
ssavaluetypes[block_end] = Union{}
stmtinfo[block_end] = NoCallInfo()
ssaflags[block_end] = IR_FLAG_NOTHROW

# Verify that type-inference did its job
if block_end > idx
if is_asserts()
# Verify that type-inference did its job
for i = (oldidx + 1):last(sv.cfg.blocks[block].stmts)
@assert i in sv.unreachable
end
end

code[block_end] = ReturnNode()
codelocs[3block_end-2], codelocs[3block_end-1], codelocs[3block_end-0] = (codelocs[3idx-2], codelocs[3idx-1], codelocs[3idx-0])
ssavaluetypes[block_end] = Union{}
stmtinfo[block_end] = NoCallInfo()
ssaflags[block_end] = IR_FLAG_NOTHROW
idx += block_end - idx
else
insert!(code, idx + 1, ReturnNode())
Expand Down Expand Up @@ -1221,6 +1220,7 @@ function convert_to_ircode(ci::CodeInfo, sv::OptimizationState)
idx += 1
oldidx += 1
end
empty!(sv.unreachable)

if ssachangemap !== nothing && labelchangemap !== nothing
renumber_ir_elements!(code, ssachangemap, labelchangemap)
Expand Down
10 changes: 4 additions & 6 deletions base/compiler/typeinfer.jl
Original file line number Diff line number Diff line change
Expand Up @@ -369,20 +369,17 @@ function cache_result!(interp::AbstractInterpreter, result::InferenceResult)
# we can now widen our applicability in the global cache too
result.valid_worlds = WorldRange(first(result.valid_worlds), typemax(UInt))
end
@assert isdefined(result.ci, :inferred)
# check if the existing linfo metadata is also sufficient to describe the current inference result
# to decide if it is worth caching this right now
mi = result.linfo
cache_results = true
cache = WorldView(code_cache(interp), result.valid_worlds)
if cache_results && haskey(cache, mi)
ci = cache[mi]
# Even if we already have a CI for this, it's possible that the new CI has more
# information (E.g. because the source was limited before, but is no longer - this
# happens during bootstrap). In that case, allow the result to be recached.
# n.b.: accurate edge representation might cause the CodeInstance for this to be constructed later
if isdefined(ci, :inferred)
cache_results = false
end
@assert isdefined(ci, :inferred)
cache_results = false
end

if cache_results
Expand Down Expand Up @@ -1205,6 +1202,7 @@ function typeinf_ext(interp::AbstractInterpreter, mi::MethodInstance, source_mod
if source_mode == SOURCE_MODE_ABI && frame.cache_mode != CACHE_MODE_GLOBAL
# XXX: jl_type_infer somewhat ambiguously assumes this must be cached, while jl_ci_cache_lookup sort of ambiguously re-caches it
# XXX: this should be using the CI from the cache, if possible instead: haskey(cache, mi) && (ci = cache[mi])
@assert isdefined(ci, :inferred) "interpreter did not fulfill its requirements"
code_cache(interp)[mi] = ci
end
if source_mode == SOURCE_MODE_FORCE_SOURCE && use_const_api(ci)
Expand Down
Loading

0 comments on commit 1add163

Please sign in to comment.