diff --git a/Compiler/src/ssair/passes.jl b/Compiler/src/ssair/passes.jl
index 1db3b84afd882..97f8f14b39f50 100644
--- a/Compiler/src/ssair/passes.jl
+++ b/Compiler/src/ssair/passes.jl
@@ -872,14 +872,15 @@ function perform_lifting!(compact::IncrementalCompact,
return Pair{Any, PhiNest}(stmt_val, PhiNest(visited_philikes, lifted_philikes, lifted_leaves, reverse_mapping, walker_callback))
end
-function lift_apply_args!(compact::IncrementalCompact, idx::Int, stmt::Expr, 𝕃ₒ::AbstractLattice)
- # Handle _apply_iterate calls: convert arguments to use `Core.svec`. The behavior of Core.svec (with boxing) better matches the ABI of codegen.
+# Handle _apply_iterate calls: convert arguments to use `Core.svec`.
+# The behavior of `Core.svec` (with boxing) better matches the ABI of codegen.
+function lift_apply_args!(compact::IncrementalCompact, idx::Int, stmt::Expr)
compact[idx] = nothing
- for i in 4:length(stmt.args) # Skip iterate function, f, and first iterator
+ for i in 4:length(stmt.args) # Skip `_apply_iterate`, `iterate`, and the function
arg = stmt.args[i]
- arg_type = argextype(arg, compact)
- svec_args = nothing
+ arg_type = widenconst(argextype(arg, compact))
if isa(arg_type, DataType) && arg_type.name === Tuple.name
+ svec_args = nothing
if isa(arg, SSAValue)
arg_stmt = compact[arg][:stmt]
if is_known_call(arg_stmt, Core.tuple, compact)
@@ -900,15 +901,14 @@ function lift_apply_args!(compact::IncrementalCompact, idx::Int, stmt::Expr,
end
end
end
- end
- # Create Core.svec call if we have arguments
- if svec_args !== nothing
- svec_args[1] = GlobalRef(Core, :svec)
- new_svec_call = Expr(:call)
- new_svec_call.args = svec_args
- inst = compact[SSAValue(idx)]
- new_svec_ssa = insert_node!(compact, SSAValue(idx), NewInstruction(new_svec_call, SimpleVector, NoCallInfo(), inst[:line], inst[:flag]))
- stmt.args[i] = new_svec_ssa
+ if svec_args !== nothing
+ svec_args[1] = GlobalRef(Core, :svec)
+ new_svec_call = Expr(:call)
+ new_svec_call.args = svec_args
+ inst = compact[SSAValue(idx)]
+ new_svec_ssa = insert_node!(compact, SSAValue(idx), NewInstruction(new_svec_call, SimpleVector, NoCallInfo(), inst[:line], inst[:flag]))
+ stmt.args[i] = new_svec_ssa
+ end
end
end
compact[idx] = stmt
@@ -1420,7 +1420,7 @@ function sroa_pass!(ir::IRCode, inlining::Union{Nothing,InliningState}=nothing)
refine_new_effects!(𝕃ₒ, compact, idx, stmt)
elseif is_known_call(stmt, Core._apply_iterate, compact)
length(stmt.args) >= 4 || continue
- lift_apply_args!(compact, idx, stmt, 𝕃ₒ)
+ lift_apply_args!(compact, idx, stmt)
end
continue
end
diff --git a/Compiler/test/irpasses.jl b/Compiler/test/irpasses.jl
index 4593aa3223902..77a66ec6435fa 100644
--- a/Compiler/test/irpasses.jl
+++ b/Compiler/test/irpasses.jl
@@ -2045,3 +2045,17 @@ let src = code_typed1(()) do
end
@test count(iscall((src, setfield!)), src.code) == 1
end
+
+# JuliaLang/julia #59548
+# Rewrite `Core._apply_iterate` to use `Core.svec` instead of `tuple` to better match
+# the codegen ABI
+let src = code_typed1((Vector{Any},)) do xs
+ println(stdout, xs...)
+ end
+ @test count(iscall((src, Core.svec)), src.code) == 1
+end
+let src = code_typed1((Vector{Any},)) do xs
+ println(stdout, 1, xs...) # convert tuples represented by `PartialStruct`
+ end
+ @test count(iscall((src, Core.svec)), src.code) == 1
+end
diff --git a/base/client.jl b/base/client.jl
index 34c0fb828e978..5aeca3f09fbcc 100644
--- a/base/client.jl
+++ b/base/client.jl
@@ -267,10 +267,6 @@ function exec_options(opts)
interactiveinput = (repl || is_interactive::Bool) && isa(stdin, TTY)
is_interactive::Bool |= interactiveinput
- # load terminfo in for styled printing
- term_env = get(ENV, "TERM", @static Sys.iswindows() ? "" : "dumb")
- global current_terminfo = load_terminfo(term_env)
-
# load ~/.julia/config/startup.jl file
if startup
try
diff --git a/base/lock.jl b/base/lock.jl
index 2da4d47c3734b..e74da826900a6 100644
--- a/base/lock.jl
+++ b/base/lock.jl
@@ -767,7 +767,7 @@ end
# share a lock/condition, since we just need it briefly, so some contention is okay
-const PerThreadLock = ThreadSynchronizer()
+const PerThreadLock = Threads.SpinLock()
"""
OncePerThread{T}(init::Function)() -> T
@@ -871,7 +871,15 @@ OncePerThread(initializer) = OncePerThread{Base.promote_op(initializer), typeof(
state = @atomic :monotonic ss[tid]
while state == PerStateConcurrent
# lost race, wait for notification this is done running elsewhere
- wait(PerThreadLock) # wait for initializer to finish without releasing this thread
+ # without releasing this thread
+ unlock(PerThreadLock)
+ while state == PerStateConcurrent
+ # spin loop until ready
+ ss = @atomic :acquire once.ss
+ state = @atomic :monotonic ss[tid]
+ GC.safepoint()
+ end
+ lock(PerThreadLock)
ss = @atomic :monotonic once.ss
state = @atomic :monotonic ss[tid]
end
@@ -885,7 +893,6 @@ OncePerThread(initializer) = OncePerThread{Base.promote_op(initializer), typeof(
lock(PerThreadLock)
ss = @atomic :monotonic once.ss
@atomic :release ss[tid] = PerStateErrored
- notify(PerThreadLock)
rethrow()
end
# store result and notify waiters
@@ -894,7 +901,6 @@ OncePerThread(initializer) = OncePerThread{Base.promote_op(initializer), typeof(
@atomic :release xs[tid] = result
ss = @atomic :monotonic once.ss
@atomic :release ss[tid] = PerStateHasrun
- notify(PerThreadLock)
elseif state == PerStateErrored
error("OncePerThread initializer failed previously")
elseif state != PerStateHasrun
diff --git a/base/reduce.jl b/base/reduce.jl
index 2a6a268f2e6c1..9474f26a174ee 100644
--- a/base/reduce.jl
+++ b/base/reduce.jl
@@ -212,7 +212,7 @@ Like [`mapreduce`](@ref), but with guaranteed right associativity, as in [`foldr
provided, the keyword argument `init` will be used exactly once. In general, it will be
necessary to provide `init` to work with empty collections.
"""
-mapfoldr(f, op, itr; init=_InitialValue()) = mapfoldr_impl(f, op, init, itr)
+mapfoldr(f::F, op::F2, itr; init=_InitialValue()) where {F,F2} = mapfoldr_impl(f, op, init, itr)
"""
@@ -231,7 +231,7 @@ julia> foldr(=>, 1:4; init=0)
1 => (2 => (3 => (4 => 0)))
```
"""
-foldr(op, itr; kw...) = mapfoldr(identity, op, itr; kw...)
+foldr(op::F, itr; kw...) where {F} = mapfoldr(identity, op, itr; kw...)
## reduce & mapreduce
diff --git a/base/strings/annotated_io.jl b/base/strings/annotated_io.jl
index 9698fd5909b68..efa84b6b99304 100644
--- a/base/strings/annotated_io.jl
+++ b/base/strings/annotated_io.jl
@@ -200,6 +200,8 @@ function _insert_annotations!(io::AnnotatedIOBuffer, annotations::Vector{RegionA
end
end
+function printstyled end
+
# NOTE: This is an interim solution to the invalidations caused
# by the split styled display implementation. This should be
# replaced by a more robust solution (such as a consolidation of
@@ -250,6 +252,14 @@ Base.print(io::AnnotatedIOBuffer, s::Union{<:AnnotatedString, SubString{<:Annota
Base.print(io::AnnotatedIOBuffer, c::AnnotatedChar) =
(write(io, c); nothing)
+styled_print(io::AnnotatedIOBuffer, msg::Any, kwargs::Any) = print(io, msg...)
+
+styled_print_(io::AnnotatedIOBuffer, @nospecialize(msg), @nospecialize(kwargs)) =
+ invoke_in_world(tls_world_age(), styled_print, io, msg, kwargs)::Nothing
+
+Base.printstyled(io::AnnotatedIOBuffer, msg...; kwargs...) =
+ styled_print_(io, msg, kwargs)
+
# Escape
Base.escape_string(io::IO, s::Union{<:AnnotatedString, SubString{<:AnnotatedString}},
diff --git a/base/terminfo.jl b/base/terminfo.jl
index 8ea8387077d36..5439a5d9868a3 100644
--- a/base/terminfo.jl
+++ b/base/terminfo.jl
@@ -303,16 +303,24 @@ end
"""
The terminfo of the current terminal.
"""
-current_terminfo::TermInfo = TermInfo()
+const current_terminfo = OncePerProcess{TermInfo}() do
+ term_env = get(ENV, "TERM", @static Sys.iswindows() ? "" : "dumb")
+ terminfo = load_terminfo(term_env)
+ # Ensure setaf is set for xterm terminals
+ if !haskey(terminfo, :setaf) && startswith(term_env, "xterm")
+ # For xterm-like terminals without setaf, add a reasonable default
+ terminfo.strings[:setaf] = "\e[3%p1%dm"
+ end
+ return terminfo
+end
# Legacy/TTY methods and the `:color` parameter
if Sys.iswindows()
- ttyhascolor(term_type = nothing) = true
+ ttyhascolor() = true
else
- function ttyhascolor(term_type = get(ENV, "TERM", ""))
- startswith(term_type, "xterm") ||
- haskey(current_terminfo, :setaf)
+ function ttyhascolor()
+ haskey(current_terminfo(), :setaf)
end
end
@@ -352,9 +360,9 @@ Multiple conditions are taken as signifying truecolor support, specifically any
function ttyhastruecolor()
# Lasciate ogne speranza, voi ch'intrate
get(ENV, "COLORTERM", "") ∈ ("truecolor", "24bit") ||
- get(current_terminfo, :RGB, false) || get(current_terminfo, :Tc, false) ||
- (haskey(current_terminfo, :setrgbf) && haskey(current_terminfo, :setrgbb)) ||
- @static if Sys.isunix() get(current_terminfo, :colors, 0) > 256 else false end ||
+ get(current_terminfo(), :RGB, false) || get(current_terminfo(), :Tc, false) ||
+ (haskey(current_terminfo(), :setrgbf) && haskey(current_terminfo(), :setrgbb)) ||
+ @static if Sys.isunix() get(current_terminfo(), :colors, 0) > 256 else false end ||
(Sys.iswindows() && Sys.windows_version() ≥ v"10.0.14931") || # See
something(tryparse(Int, get(ENV, "VTE_VERSION", "")), 0) >= 3600 || # Per GNOME bug #685759
haskey(ENV, "XTERM_VERSION") ||
diff --git a/base/timing.jl b/base/timing.jl
index 9e3a4cf128413..e937d396a52a2 100644
--- a/base/timing.jl
+++ b/base/timing.jl
@@ -472,41 +472,93 @@ function gc_bytes()
b[]
end
-function allocated(f, args::Vararg{Any,N}) where {N}
+@constprop :none function allocated(f, args::Vararg{Any,N}) where {N}
b0 = Ref{Int64}(0)
b1 = Ref{Int64}(0)
Base.gc_bytes(b0)
- f(args...)
+ @noinline f(args...)
Base.gc_bytes(b1)
return b1[] - b0[]
end
only(methods(allocated)).called = 0xff
-function allocations(f, args::Vararg{Any,N}) where {N}
+@constprop :none function allocations(f, args::Vararg{Any,N}) where {N}
stats = Base.gc_num()
- f(args...)
+ @noinline f(args...)
diff = Base.GC_Diff(Base.gc_num(), stats)
return Base.gc_alloc_count(diff)
end
only(methods(allocations)).called = 0xff
function is_simply_call(@nospecialize ex)
+ is_simple_atom(a) = a isa QuoteNode || a isa Symbol || is_self_quoting(a)
Meta.isexpr(ex, :call) || return false
for a in ex.args
- a isa QuoteNode && continue
- a isa Symbol && continue
- Base.is_self_quoting(a) && continue
+ is_simple_atom(a) && continue
+ Meta.isexpr(a, :..., 1) && is_simple_atom(a.args[1]) && continue
return false
end
return true
end
+function _gen_allocation_measurer(ex, fname::Symbol)
+ if isexpr(ex, :call)
+ if !is_simply_call(ex)
+ ex = :((() -> $ex)())
+ end
+ pushfirst!(ex.args, GlobalRef(Base, fname))
+ return quote
+ Experimental.@force_compile
+ $(esc(ex))
+ end
+ elseif fname === :allocated
+ # v1.11-compatible implementation
+ return quote
+ Experimental.@force_compile
+ local b0 = Ref{Int64}(0)
+ local b1 = Ref{Int64}(0)
+ gc_bytes(b0)
+ $(esc(ex))
+ gc_bytes(b1)
+ b1[] - b0[]
+ end
+ else
+ @assert fname === :allocations
+ return quote
+ Experimental.@force_compile
+ # Note this value is unused, but without it `allocated` and `allocations`
+ # are sufficiently different that the compiler can remove allocations here
+ # that it cannot remove there, giving inconsistent numbers.
+ local b1 = Ref{Int64}(0)
+ local stats = Base.gc_num()
+ $(esc(ex))
+ local diff = Base.GC_Diff(Base.gc_num(), stats)
+ gc_bytes(b1)
+ Base.gc_alloc_count(diff)
+ end
+ end
+end
+
"""
@allocated
A macro to evaluate an expression, discarding the resulting value, instead returning the
total number of bytes allocated during evaluation of the expression.
+If the expression is a function call, an effort is made to measure only allocations from
+the argument expressions and during the function, excluding any overhead from calling it
+and not performing constant propagation with the provided argument values. If you want to
+include those effects, i.e. measuring the call site as well, use the syntax
+`@allocated (()->f(1))()`.
+
+It is recommended to measure function calls with only simple argument expressions, e.g.
+`x = []; @allocated f(x)` instead of `@allocated f([])` to clarify that only `f` is
+being measured.
+
+For more complex expressions, the code is simply run in place and therefore may see
+allocations due to the surrounding context. For example it is possible for
+`@allocated f(1)` and `@allocated x = f(1)` to give different results.
+
See also [`@allocations`](@ref), [`@time`](@ref), [`@timev`](@ref), [`@timed`](@ref),
and [`@elapsed`](@ref).
@@ -516,11 +568,7 @@ julia> @allocated rand(10^6)
```
"""
macro allocated(ex)
- if !is_simply_call(ex)
- ex = :((() -> $ex)())
- end
- pushfirst!(ex.args, GlobalRef(Base, :allocated))
- return esc(ex)
+ _gen_allocation_measurer(ex, :allocated)
end
"""
@@ -541,11 +589,7 @@ julia> @allocations rand(10^6)
This macro was added in Julia 1.9.
"""
macro allocations(ex)
- if !is_simply_call(ex)
- ex = :((() -> $ex)())
- end
- pushfirst!(ex.args, GlobalRef(Base, :allocations))
- return esc(ex)
+ _gen_allocation_measurer(ex, :allocations)
end
diff --git a/deps/checksums/LinearAlgebra-24f5e21cf3a560ca560c5a1759ff21ba68382ebd.tar.gz/md5 b/deps/checksums/LinearAlgebra-24f5e21cf3a560ca560c5a1759ff21ba68382ebd.tar.gz/md5
new file mode 100644
index 0000000000000..9b1701abea5dc
--- /dev/null
+++ b/deps/checksums/LinearAlgebra-24f5e21cf3a560ca560c5a1759ff21ba68382ebd.tar.gz/md5
@@ -0,0 +1 @@
+93162fc479ba1762028ef917176f45e0
diff --git a/deps/checksums/LinearAlgebra-24f5e21cf3a560ca560c5a1759ff21ba68382ebd.tar.gz/sha512 b/deps/checksums/LinearAlgebra-24f5e21cf3a560ca560c5a1759ff21ba68382ebd.tar.gz/sha512
new file mode 100644
index 0000000000000..721f154ad5366
--- /dev/null
+++ b/deps/checksums/LinearAlgebra-24f5e21cf3a560ca560c5a1759ff21ba68382ebd.tar.gz/sha512
@@ -0,0 +1 @@
+d6c421048e52d5cf32848cf7a16db1ac269f2c553672b0f1126230a7f5954adb6f2883982efe747bd91fa8135071e1b2f717a12ceb1631c1c7effbf6cece8f4c
diff --git a/deps/checksums/LinearAlgebra-b5a8bb07059f0d499c493c6db01980b060836f5a.tar.gz/md5 b/deps/checksums/LinearAlgebra-b5a8bb07059f0d499c493c6db01980b060836f5a.tar.gz/md5
deleted file mode 100644
index b8651ee8f2d46..0000000000000
--- a/deps/checksums/LinearAlgebra-b5a8bb07059f0d499c493c6db01980b060836f5a.tar.gz/md5
+++ /dev/null
@@ -1 +0,0 @@
-f41f7e4247fddb5531dea530bee83024
diff --git a/deps/checksums/LinearAlgebra-b5a8bb07059f0d499c493c6db01980b060836f5a.tar.gz/sha512 b/deps/checksums/LinearAlgebra-b5a8bb07059f0d499c493c6db01980b060836f5a.tar.gz/sha512
deleted file mode 100644
index 6523086dbd0a1..0000000000000
--- a/deps/checksums/LinearAlgebra-b5a8bb07059f0d499c493c6db01980b060836f5a.tar.gz/sha512
+++ /dev/null
@@ -1 +0,0 @@
-d2596e62f75841be0da013c8c41708577ae2527d3564d95081385d77fc00da514a46f25bbb42304b76127890018358cb191c80c18ba57818c2fde8ebedde260b
diff --git a/deps/checksums/SparseArrays-5d674dc7bd90156cf8ecea4e143b69b5a5b7640d.tar.gz/md5 b/deps/checksums/SparseArrays-5d674dc7bd90156cf8ecea4e143b69b5a5b7640d.tar.gz/md5
new file mode 100644
index 0000000000000..ab3e4ebfe9f25
--- /dev/null
+++ b/deps/checksums/SparseArrays-5d674dc7bd90156cf8ecea4e143b69b5a5b7640d.tar.gz/md5
@@ -0,0 +1 @@
+eadaa92895c8d4d33eb601165ef765d5
diff --git a/deps/checksums/SparseArrays-5d674dc7bd90156cf8ecea4e143b69b5a5b7640d.tar.gz/sha512 b/deps/checksums/SparseArrays-5d674dc7bd90156cf8ecea4e143b69b5a5b7640d.tar.gz/sha512
new file mode 100644
index 0000000000000..7a250e67b00d3
--- /dev/null
+++ b/deps/checksums/SparseArrays-5d674dc7bd90156cf8ecea4e143b69b5a5b7640d.tar.gz/sha512
@@ -0,0 +1 @@
+bb37377b360eca1a32c78b1d11b83c7e918a8ddb9df79388694b6f415dc5d5cf6182df7437869b3970011e5dcda4a3f821b58498bfa6fd7df697fcd51383ca12
diff --git a/deps/checksums/SparseArrays-cdbad55530fba0c7aa27d4bcc64dde2204ff133f.tar.gz/md5 b/deps/checksums/SparseArrays-cdbad55530fba0c7aa27d4bcc64dde2204ff133f.tar.gz/md5
deleted file mode 100644
index 0cd5ebfb0fd87..0000000000000
--- a/deps/checksums/SparseArrays-cdbad55530fba0c7aa27d4bcc64dde2204ff133f.tar.gz/md5
+++ /dev/null
@@ -1 +0,0 @@
-9f4376c0de171f481c153442dee250a4
diff --git a/deps/checksums/SparseArrays-cdbad55530fba0c7aa27d4bcc64dde2204ff133f.tar.gz/sha512 b/deps/checksums/SparseArrays-cdbad55530fba0c7aa27d4bcc64dde2204ff133f.tar.gz/sha512
deleted file mode 100644
index 207b0685cc2a5..0000000000000
--- a/deps/checksums/SparseArrays-cdbad55530fba0c7aa27d4bcc64dde2204ff133f.tar.gz/sha512
+++ /dev/null
@@ -1 +0,0 @@
-ee756aa32d95849f728f7cfae8ba9cfce8ad51701cee6430e547f6b80b1f420e78d7d74471afc25ffc8cf6699cd5ae4e63cf38c32f3e8279179630074fda1830
diff --git a/deps/checksums/StyledStrings-3fe829fcf611b5fefaefb64df7e61f2ae82db117.tar.gz/md5 b/deps/checksums/StyledStrings-3fe829fcf611b5fefaefb64df7e61f2ae82db117.tar.gz/md5
deleted file mode 100644
index 46d5cacf788df..0000000000000
--- a/deps/checksums/StyledStrings-3fe829fcf611b5fefaefb64df7e61f2ae82db117.tar.gz/md5
+++ /dev/null
@@ -1 +0,0 @@
-1cb6007a66d3f74cbe5b27ee449aa9c8
diff --git a/deps/checksums/StyledStrings-3fe829fcf611b5fefaefb64df7e61f2ae82db117.tar.gz/sha512 b/deps/checksums/StyledStrings-3fe829fcf611b5fefaefb64df7e61f2ae82db117.tar.gz/sha512
deleted file mode 100644
index 724b2d311c123..0000000000000
--- a/deps/checksums/StyledStrings-3fe829fcf611b5fefaefb64df7e61f2ae82db117.tar.gz/sha512
+++ /dev/null
@@ -1 +0,0 @@
-1fa95646fdf4cc7ea282bd355fded9464e7572792912942ea1c45f6ed126eead2333fdeed92e7db3efbcd6c3a171a04e5c9562dab2685bb39947136284ae1da3
diff --git a/deps/checksums/StyledStrings-68bf7b1f83f334391dc05fda34f48267e04e2bd0.tar.gz/md5 b/deps/checksums/StyledStrings-68bf7b1f83f334391dc05fda34f48267e04e2bd0.tar.gz/md5
new file mode 100644
index 0000000000000..e58ba4519f3ff
--- /dev/null
+++ b/deps/checksums/StyledStrings-68bf7b1f83f334391dc05fda34f48267e04e2bd0.tar.gz/md5
@@ -0,0 +1 @@
+ad2e6ba06c98990865f808b26b8f148c
diff --git a/deps/checksums/StyledStrings-68bf7b1f83f334391dc05fda34f48267e04e2bd0.tar.gz/sha512 b/deps/checksums/StyledStrings-68bf7b1f83f334391dc05fda34f48267e04e2bd0.tar.gz/sha512
new file mode 100644
index 0000000000000..8f7b0e2111bce
--- /dev/null
+++ b/deps/checksums/StyledStrings-68bf7b1f83f334391dc05fda34f48267e04e2bd0.tar.gz/sha512
@@ -0,0 +1 @@
+24e530c095f7838380adeb6f45349cf776df524a2fc721eb8b11411d25bc132a58c1048a89d630ba1ee66bf9a52cce9a0fbe2b4a76c33b11160c00ecb7a919a1
diff --git a/src/gf.c b/src/gf.c
index 23b863294b45b..b13830c1e1ba9 100644
--- a/src/gf.c
+++ b/src/gf.c
@@ -1482,10 +1482,14 @@ static int concretesig_equal(jl_value_t *tt, jl_value_t *simplesig) JL_NOTSAFEPO
return 1;
}
+// if available, returns a TypeMapEntry in the "leafcache" that matches `tt` (by type-equality) and is valid during `world`
static inline jl_typemap_entry_t *lookup_leafcache(jl_genericmemory_t *leafcache JL_PROPAGATES_ROOT, jl_value_t *tt, size_t world) JL_NOTSAFEPOINT
{
jl_typemap_entry_t *entry = (jl_typemap_entry_t*)jl_eqtable_get(leafcache, (jl_value_t*)tt, NULL);
if (entry) {
+ // search tail of the linked-list (including the returned entry) for an entry intersecting world
+ //
+ // n.b. this entire chain is type-equal to tt (by construction), so it is unnecessary to call `tt<:entry->sig`
do {
if (jl_atomic_load_relaxed(&entry->min_world) <= world && world <= jl_atomic_load_relaxed(&entry->max_world)) {
if (entry->simplesig == (void*)jl_nothing || concretesig_equal(tt, (jl_value_t*)entry->simplesig))
@@ -4389,21 +4393,31 @@ static jl_method_match_t *make_method_match(jl_tupletype_t *spec_types, jl_svec_
return match;
}
+// callback for typemap_visitor
+//
+// This will exit the search early (by returning 0 / false) if the match limit is proven to be
+// exceeded early. This is only best-effort, since specificity means that many matched methods
+// may be sorted and removed in the output processing for ml_matches and therefore we can only
+// conservatively under-approximate the matches during the search.
static int ml_matches_visitor(jl_typemap_entry_t *ml, struct typemap_intersection_env *closure0)
{
struct ml_matches_env *closure = container_of(closure0, struct ml_matches_env, match);
if (closure->intersections == 0 && !closure0->issubty)
return 1;
+
+ // First, check the world range of the typemap entry to ensure that it intersects
+ // the query world. If it does not, narrow the result world range to guarantee
+ // excluding it from the results is valid for the full span.
size_t min_world = jl_atomic_load_relaxed(&ml->min_world);
size_t max_world = jl_atomic_load_relaxed(&ml->max_world);
if (closure->world < min_world) {
- // ignore method table entries that are part of a later world
+ // exclude method table entries that are part of a later world
if (closure->match.max_valid >= min_world)
closure->match.max_valid = min_world - 1;
return 1;
}
else if (closure->world > max_world) {
- // ignore method table entries that have been replaced in the current world
+ // exclude method table entries that have been replaced in the current world
if (closure->match.min_valid <= max_world)
closure->match.min_valid = max_world + 1;
return 1;
@@ -4601,21 +4615,47 @@ static jl_value_t *ml_matches(jl_methtable_t *mt, jl_methcache_t *mc,
else
va = NULL;
}
- struct ml_matches_env env = {{ml_matches_visitor, (jl_value_t*)type, va, /* .search_slurp = */ 0,
- /* .min_valid = */ *min_valid, /* .max_valid = */ *max_valid,
- /* .ti = */ NULL, /* .env = */ jl_emptysvec, /* .issubty = */ 0},
- intersections, world, lim, include_ambiguous, /* .t = */ jl_an_empty_vec_any,
- /* .matc = */ NULL};
+ struct ml_matches_env env = {
+ /* match */ {
+ /* inputs */
+ /* fptr / callback */ ml_matches_visitor,
+ /* sig */ (jl_value_t*)type,
+ /* vararg type / tparam0 */ va,
+
+ /* temporaries */
+ /* .search_slurp = */ 0,
+
+ /* outputs */
+ /* .min_valid = */ *min_valid,
+ /* .max_valid = */ *max_valid,
+ /* .ti = */ NULL,
+ /* .env = */ jl_emptysvec,
+ /* .issubty = */ 0
+ },
+ /* inputs */
+ intersections,
+ world,
+ lim,
+ include_ambiguous,
+
+ /* outputs */
+ /* .t = */ jl_an_empty_vec_any,
+
+ /* temporaries */
+ /* .matc = */ NULL
+ };
struct jl_typemap_assoc search = {(jl_value_t*)type, world, jl_emptysvec};
jl_value_t *isect2 = NULL;
JL_GC_PUSH6(&env.t, &env.matc, &env.match.env, &search.env, &env.match.ti, &isect2);
if (mc) {
- // check the leaf cache if this type can be in there
+ // first check the leaf cache if the type might have been put in there
if (((jl_datatype_t*)unw)->isdispatchtuple) {
jl_genericmemory_t *leafcache = jl_atomic_load_relaxed(&mc->leafcache);
jl_typemap_entry_t *entry = lookup_leafcache(leafcache, (jl_value_t*)type, world);
if (entry) {
+ // leafcache found a match, construct the MethodMatch by computing the effective
+ // types + sparams and the world bounds
jl_method_instance_t *mi = entry->func.linfo;
jl_method_t *meth = mi->def.method;
if (!jl_is_unionall(meth->sig)) {
@@ -4644,10 +4684,13 @@ static jl_value_t *ml_matches(jl_methtable_t *mt, jl_methcache_t *mc,
return env.t;
}
}
+
// then check the full cache if it seems profitable
if (((jl_datatype_t*)unw)->isdispatchtuple) {
jl_typemap_entry_t *entry = jl_typemap_assoc_by_type(jl_atomic_load_relaxed(&mc->cache), &search, jl_cachearg_offset(), /*subtype*/1);
if (entry && (((jl_datatype_t*)unw)->isdispatchtuple || entry->guardsigs == jl_emptysvec)) {
+ // full cache found a match, construct the MethodMatch by computing the effective
+ // types + sparams and the world bounds
jl_method_instance_t *mi = entry->func.linfo;
jl_method_t *meth = mi->def.method;
size_t min_world = jl_atomic_load_relaxed(&entry->min_world);
@@ -4679,7 +4722,8 @@ static jl_value_t *ml_matches(jl_methtable_t *mt, jl_methcache_t *mc,
// then scan everything
if (!jl_typemap_intersection_visitor(jl_atomic_load_relaxed(&mt->defs), 0, &env.match) && env.t == jl_an_empty_vec_any) {
JL_GC_POP();
- // if we return early without returning methods, set only the min/max valid collected from matching
+ // if we return early without returning methods, lim was proven to be exceeded
+ // during the search set only the min/max valid collected from matching
*min_valid = env.match.min_valid;
*max_valid = env.match.max_valid;
return jl_nothing;
@@ -4689,12 +4733,19 @@ static jl_value_t *ml_matches(jl_methtable_t *mt, jl_methcache_t *mc,
*max_valid = env.match.max_valid;
// done with many of these values now
env.match.ti = NULL; env.matc = NULL; env.match.env = NULL; search.env = NULL;
+
+ // all intersecting methods have been collected now. the remaining work is to sort
+ // these and apply specificity to determine a list of dispatch-possible call targets
size_t i, j, len = jl_array_nrows(env.t);
+
+ // the 'minmax' method is a method that (1) fully-covers the queried type, and (2) is
+ // more-specific than any other fully-covering method (but if !all_subtypes, there are
+ // non-fully-covering methods to which it is _likely_ not more specific)
jl_method_match_t *minmax = NULL;
int any_subtypes = 0;
if (len > 1) {
- // first try to pre-process the results to find the most specific
- // result that fully covers the input, since we can do this in O(n^2)
+ // first try to pre-process the results to find the most specific option
+ // among the fully-covering methods, since we can do this in O(n^2)
// time, and the rest is O(n^3)
// - first find a candidate for the best of these method results
for (i = 0; i < len; i++) {
@@ -4719,8 +4770,8 @@ static jl_value_t *ml_matches(jl_methtable_t *mt, jl_methcache_t *mc,
}
}
}
- // - it may even dominate some choices that are not subtypes!
- // move those into the subtype group, where we're filter them out shortly after
+ // - it may even dominate (be more specific than) some choices that are not fully-covering!
+ // move those into the subtype group, where we'll filter them out shortly after
// (potentially avoiding reporting these as an ambiguity, and
// potentially allowing us to hit the next fast path)
// - we could always check here if *any* FULLY_COVERS method is
@@ -4733,6 +4784,8 @@ static jl_value_t *ml_matches(jl_methtable_t *mt, jl_methcache_t *mc,
jl_method_t *minmaxm = NULL;
if (minmax != NULL)
minmaxm = minmax->method;
+ // scan through all the non-fully-matching methods and count them as "fully-covering" (ish)
+ // (i.e. in the 'subtype' group) if `minmax` is more-specific
for (i = 0; i < len; i++) {
jl_method_match_t *matc = (jl_method_match_t*)jl_array_ptr_ref(env.t, i);
if (matc->fully_covers != FULLY_COVERS) {
@@ -4753,16 +4806,21 @@ static jl_value_t *ml_matches(jl_methtable_t *mt, jl_methcache_t *mc,
// we've already processed all of the possible outputs
if (all_subtypes) {
if (minmax == NULL) {
+ // all intersecting methods are fully-covering, but there is no unique most-specific method
if (!include_ambiguous) {
+ // there no unambiguous choice of method
len = 0;
env.t = jl_an_empty_vec_any;
}
else if (lim == 1) {
+ // we'd have to return >1 method due to the ambiguity, so bail early
JL_GC_POP();
return jl_nothing;
}
}
else {
+ // `minmax` is more-specific than all other matches and is fully-covering
+ // we can return it as our only result
jl_array_ptr_set(env.t, 0, minmax);
jl_array_del_end((jl_array_t*)env.t, len - 1);
len = 1;
diff --git a/src/julia.h b/src/julia.h
index d5ffa9575a69a..e8322119299a3 100644
--- a/src/julia.h
+++ b/src/julia.h
@@ -883,14 +883,20 @@ typedef struct _jl_typemap_level_t {
typedef struct _jl_methcache_t {
JL_DATA_TYPE
+ // hash map from dispatchtuple type to a linked-list of TypeMapEntry
+ // entry.sig == type for all entries in the linked-list
_Atomic(jl_genericmemory_t*) leafcache;
+
+ // cache for querying everything else (anything that didn't seem profitable to put into leafcache)
_Atomic(jl_typemap_t*) cache;
+
jl_mutex_t writelock;
} jl_methcache_t;
// contains global MethodTable
typedef struct _jl_methtable_t {
JL_DATA_TYPE
+ // full set of entries
_Atomic(jl_typemap_t*) defs;
jl_methcache_t *cache;
jl_sym_t *name; // sometimes used for debug printing
diff --git a/src/typemap.c b/src/typemap.c
index 8e67428391aef..f4e0520291816 100644
--- a/src/typemap.c
+++ b/src/typemap.c
@@ -1331,6 +1331,9 @@ static void jl_typemap_list_insert_(
jl_typemap_entry_t *newrec)
{
jl_typemap_entry_t *l = jl_atomic_load_relaxed(pml);
+
+ // Pick the first intersection point that guarantees that the list ordering
+ // will be (leaf sigs..., simple sigs..., other sigs...)
while ((jl_value_t*)l != jl_nothing) {
if (newrec->isleafsig || !l->isleafsig)
if (newrec->issimplesig || !l->issimplesig)
@@ -1339,6 +1342,7 @@ static void jl_typemap_list_insert_(
parent = (jl_value_t*)l;
l = jl_atomic_load_relaxed(&l->next);
}
+
jl_atomic_store_relaxed(&newrec->next, l);
jl_gc_wb(newrec, l);
jl_atomic_store_release(pml, newrec);
@@ -1356,6 +1360,7 @@ static void jl_typemap_insert_generic(
jl_typemap_memory_insert_(map, (_Atomic(jl_genericmemory_t*)*)pml, doublesplit, newrec, parent, 0, offs, NULL);
return;
}
+
if (jl_typeof(ml) == (jl_value_t*)jl_typemap_level_type) {
assert(!doublesplit);
jl_typemap_level_insert_(map, (jl_typemap_level_t*)ml, newrec, offs);
diff --git a/stdlib/LinearAlgebra.version b/stdlib/LinearAlgebra.version
index 6ee231d7dd649..b5383b0749ae0 100644
--- a/stdlib/LinearAlgebra.version
+++ b/stdlib/LinearAlgebra.version
@@ -1,4 +1,4 @@
LINEARALGEBRA_BRANCH = release-1.12
-LINEARALGEBRA_SHA1 = b5a8bb07059f0d499c493c6db01980b060836f5a
+LINEARALGEBRA_SHA1 = 24f5e21cf3a560ca560c5a1759ff21ba68382ebd
LINEARALGEBRA_GIT_URL := https://github.com/JuliaLang/LinearAlgebra.jl.git
LINEARALGEBRA_TAR_URL = https://api.github.com/repos/JuliaLang/LinearAlgebra.jl/tarball/$1
diff --git a/stdlib/Makefile b/stdlib/Makefile
index 3975f24b7ae3b..bf14c7ee834b9 100644
--- a/stdlib/Makefile
+++ b/stdlib/Makefile
@@ -15,6 +15,8 @@ include $(JULIAHOME)/deps/*.version
VERSDIR := v$(shell cut -d. -f1-2 < $(JULIAHOME)/VERSION)
DIRS := $(build_datarootdir)/julia/stdlib/$(VERSDIR) $(build_prefix)/manifest/$(VERSDIR)
+LIBDIR := $(build_datarootdir)/lib/julia
+
$(foreach dir,$(DIRS),$(eval $(call dir_target,$(dir))))
JLLS = DSFMT GMP CURL LIBGIT2 LLVM LIBSSH2 LIBUV OPENSSL MPFR NGHTTP2 \
@@ -60,8 +62,19 @@ $(foreach module, $(STDLIBS), $(eval $(call symlink_target,$$(JULIAHOME)/stdlib/
STDLIBS_LINK_TARGETS := $(addprefix $(build_datarootdir)/julia/stdlib/$(VERSDIR)/,$(STDLIBS))
+remove-gpl-libs:
+ifeq ($(USE_GPL_LIBS),0)
+ @echo Removing GPL libs...
+ -rm -f $(LIBDIR)/libcholmod*
+ -rm -f $(LIBDIR)/libklu_cholmod*
+ -rm -f $(LIBDIR)/librbio*
+ -rm -f $(LIBDIR)/libspqr*
+ -rm -f $(LIBDIR)/libumfpack*
+endif
+
getall get: $(addprefix get-, $(STDLIBS_EXT) $(JLL_NAMES))
-install: version-check $(addprefix install-, $(STDLIBS_EXT) $(JLL_NAMES)) $(STDLIBS_LINK_TARGETS)
+
+install: version-check $(addprefix install-, $(STDLIBS_EXT) $(JLL_NAMES)) $(STDLIBS_LINK_TARGETS) remove-gpl-libs
version-check: $(addprefix version-check-, $(STDLIBS_EXT))
uninstall: $(addprefix uninstall-, $(STDLIBS_EXT))
extstdlibclean:
diff --git a/stdlib/SparseArrays.version b/stdlib/SparseArrays.version
index 4e9de62c06d6f..b6d9a820d9a06 100644
--- a/stdlib/SparseArrays.version
+++ b/stdlib/SparseArrays.version
@@ -1,4 +1,4 @@
SPARSEARRAYS_BRANCH = release-1.12
-SPARSEARRAYS_SHA1 = cdbad55530fba0c7aa27d4bcc64dde2204ff133f
+SPARSEARRAYS_SHA1 = 5d674dc7bd90156cf8ecea4e143b69b5a5b7640d
SPARSEARRAYS_GIT_URL := https://github.com/JuliaSparse/SparseArrays.jl.git
SPARSEARRAYS_TAR_URL = https://api.github.com/repos/JuliaSparse/SparseArrays.jl/tarball/$1
diff --git a/stdlib/StyledStrings.version b/stdlib/StyledStrings.version
index 55a4a08c17ea0..b77e1a8cd9f59 100644
--- a/stdlib/StyledStrings.version
+++ b/stdlib/StyledStrings.version
@@ -1,4 +1,4 @@
STYLEDSTRINGS_BRANCH = main
-STYLEDSTRINGS_SHA1 = 3fe829fcf611b5fefaefb64df7e61f2ae82db117
+STYLEDSTRINGS_SHA1 = 68bf7b1f83f334391dc05fda34f48267e04e2bd0
STYLEDSTRINGS_GIT_URL := https://github.com/JuliaLang/StyledStrings.jl.git
STYLEDSTRINGS_TAR_URL = https://api.github.com/repos/JuliaLang/StyledStrings.jl/tarball/$1
diff --git a/test/boundscheck_exec.jl b/test/boundscheck_exec.jl
index a79395c88231e..1630bf7dde0fd 100644
--- a/test/boundscheck_exec.jl
+++ b/test/boundscheck_exec.jl
@@ -349,8 +349,9 @@ if bc_opt == bc_default
m2 = Memory{Int}(undef,n)
m1 === m2
end
- no_alias_prove(1)
- @test (@allocated no_alias_prove(5)) == 0
+ no_alias_prove5() = no_alias_prove(5)
+ no_alias_prove5()
+ @test (@allocated no_alias_prove5()) == 0
end
end
diff --git a/test/misc.jl b/test/misc.jl
index 60a0262473b91..8d6ee39715303 100644
--- a/test/misc.jl
+++ b/test/misc.jl
@@ -1522,15 +1522,40 @@ end
# issue #41656
run(`$(Base.julia_cmd()) -e 'isempty(x) = true'`)
+function treshape59278(X::AbstractArray, n, m)
+ Y = reshape(X, n, m)
+ Y .= 1.0
+ return X
+end
+
+# a function that allocates iff no constprop
+@inline maybealloc59278(n, _) = ntuple(i->rand(), n)
+
@testset "Base/timing.jl" begin
@test Base.jit_total_bytes() >= 0
# sanity check `@allocations` returns what we expect in some very simple cases.
- # These are inside functions because `@allocations` uses `Experimental.@force_compile`
- # so can be affected by other code in the same scope.
@test (() -> @allocations "a")() == 0
- @test (() -> @allocations "a" * "b")() == 0 # constant propagation
+ "a" * Base.inferencebarrier("b")
@test (() -> @allocations "a" * Base.inferencebarrier("b"))() == 1
+ # test that you can grab the value from @allocated
+ @allocated _x = 1+2
+ @test _x === 3
+
+ n, m = 10, 20
+ X = rand(n, m)
+ treshape59278(X, n, m)
+ # test that @allocated and @allocations are consistent about whether anything was
+ # allocated in a case where the compiler can sometimes remove an allocation
+ # https://github.com/JuliaLang/julia/issues/58634#issuecomment-2940840651
+ @test ((@allocated treshape59278(X, n, m))==0) == ((@allocations treshape59278(X, n, m))==0)
+ # TODO: would be nice to have but not yet reliable
+ #@test ((@allocated begin treshape59278(X, n, m) end)==0) == ((@allocations begin treshape59278(X, n, m) end)==0)
+
+ # test that all wrapped allocations are counted and constprop is not done
+ @test (@allocated @noinline maybealloc59278(10, [])) > (@allocated maybealloc59278(10, 0)) > 0
+ # but if you wrap it in another function it can be constprop'd
+ @test (@allocated (()->maybealloc59278(10, []))()) == 0
_lock_conflicts, _nthreads = eval(Meta.parse(read(`$(Base.julia_cmd()) -tauto -E '
_lock_conflicts = @lock_conflicts begin