From 2ca125f6f3d4e49d1ae0db762270edc1b4875bb0 Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Mon, 20 Jan 2025 10:26:49 +0900 Subject: [PATCH 01/56] docs: show binding name for `No documentation found for ...` message (#57093) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Making it more informative. E.g., the difference for `help?> Base._wait` is: ```diff diff --git a/old b/new index 87f9b2d43a..5fb065658a 100644 --- a/old +++ b/new @@ -5,7 +5,7 @@ │ │ • `Base._wait` - No documentation found for private symbol. + No documentation found for private binding `Base._wait`. `Base._wait` is a `Function`. ``` --- stdlib/REPL/src/docview.jl | 5 +++-- test/docs.jl | 40 ++++++++++++++++++++------------------ 2 files changed, 24 insertions(+), 21 deletions(-) diff --git a/stdlib/REPL/src/docview.jl b/stdlib/REPL/src/docview.jl index 0868d3e80c824..e0710d89a67e2 100644 --- a/stdlib/REPL/src/docview.jl +++ b/stdlib/REPL/src/docview.jl @@ -301,10 +301,11 @@ function summarize(binding::Binding, sig) if defined(binding) binding_res = resolve(binding) if !isa(binding_res, Module) + varstr = "$(binding.mod).$(binding.var)" if Base.ispublic(binding.mod, binding.var) - println(io, "No documentation found for public symbol.\n") + println(io, "No documentation found for public binding `$varstr`.\n") else - println(io, "No documentation found for private symbol.\n") + println(io, "No documentation found for private binding `$varstr`.\n") end end summarize(io, binding_res, binding) diff --git a/test/docs.jl b/test/docs.jl index 8cfdbba3f2d97..0fff85e90cb59 100644 --- a/test/docs.jl +++ b/test/docs.jl @@ -684,9 +684,11 @@ end @doc "This should document @m1... since its the result of expansion" @m2_11993 @test (@doc @m1_11993) !== nothing let d = (@doc :@m2_11993), - macro_doc = Markdown.parse("`$(curmod_prefix == "Main." ? "" : curmod_prefix)@m2_11993` is a macro.") + varstr = "$(curmod_prefix == "Main." ? "" : curmod_prefix)@m2_11993" + docstr = Markdown.Code("", "$curmod_prefix@m2_11993") + macro_doc = Markdown.parse("`$varstr` is a macro.") @test docstring_startswith(d, doc""" - No documentation found for private symbol. + No documentation found for private binding $docstr. $macro_doc""") end @@ -901,7 +903,7 @@ Binding `$(curmod_prefix)Undocumented.bindingdoesnotexist` does not exist. @test docstrings_equal(@doc(Undocumented.bindingdoesnotexist), doc"$doc_str") doc_str = Markdown.parse(""" -No documentation found for public symbol. +No documentation found for public binding `$(curmod_prefix)Undocumented.A`. # Summary ``` @@ -917,7 +919,7 @@ $(curmod_prefix)Undocumented.C @test docstrings_equal(@doc(Undocumented.A), doc"$doc_str") doc_str = Markdown.parse(""" -No documentation found for public symbol. +No documentation found for public binding `$(curmod_prefix)Undocumented.B`. # Summary ``` @@ -937,7 +939,7 @@ $(curmod_prefix)Undocumented.B <: $(curmod_prefix)Undocumented.A <: Any @test docstrings_equal(@doc(Undocumented.B), doc"$doc_str") doc_str = Markdown.parse(""" -No documentation found for public symbol. +No documentation found for public binding `$(curmod_prefix)Undocumented.C`. # Summary ``` @@ -952,7 +954,7 @@ $(curmod_prefix)Undocumented.C <: $(curmod_prefix)Undocumented.A <: Any @test docstrings_equal(@doc(Undocumented.C), doc"$doc_str") doc_str = Markdown.parse(""" -No documentation found for private symbol. +No documentation found for private binding `$(curmod_prefix)Undocumented.D`. # Summary ``` @@ -974,7 +976,7 @@ $(curmod_prefix)Undocumented.D <: $(curmod_prefix)Undocumented.B <: $(curmod_pre @test docstrings_equal(@doc(Undocumented.D), doc"$doc_str") doc_str = Markdown.parse(""" -No documentation found for public symbol. +No documentation found for public binding `$(curmod_prefix)Undocumented.at0`. # Summary @@ -994,7 +996,7 @@ $(curmod_prefix)Undocumented.st4{T<:Number, N} @test docstrings_equal(@doc(Undocumented.at0), doc"$doc_str") doc_str = Markdown.parse(""" -No documentation found for private symbol. +No documentation found for private binding `$(curmod_prefix)Undocumented.at1`. # Summary @@ -1017,7 +1019,7 @@ $(curmod_prefix)Undocumented.at1{T>:Integer, N} <: $(curmod_prefix)Undocumented. @test docstrings_equal(@doc(Undocumented.at1), doc"$doc_str") doc_str = Markdown.parse(""" -No documentation found for private symbol. +No documentation found for private binding `$(curmod_prefix)Undocumented.at_`. # Summary @@ -1036,7 +1038,7 @@ $(curmod_prefix)Undocumented.st4{Int64, N} @test docstrings_equal(@doc(Undocumented.at_), doc"$doc_str") doc_str = Markdown.parse(""" -No documentation found for public symbol. +No documentation found for public binding `$(curmod_prefix)Undocumented.pt2`. # Summary @@ -1053,7 +1055,7 @@ $(curmod_prefix)Undocumented.pt2{T<:Number, N, A>:Integer} <: $(curmod_prefix)Un @test docstrings_equal(@doc(Undocumented.pt2), doc"$doc_str") doc_str = Markdown.parse(""" -No documentation found for private symbol. +No documentation found for private binding `$(curmod_prefix)Undocumented.st3`. # Summary @@ -1076,7 +1078,7 @@ $(curmod_prefix)Undocumented.st3{T<:Integer, N} <: $(curmod_prefix)Undocumented. @test docstrings_equal(@doc(Undocumented.st3), doc"$doc_str") doc_str = Markdown.parse(""" -No documentation found for private symbol. +No documentation found for private binding `$(curmod_prefix)Undocumented.st4`. # Summary @@ -1098,7 +1100,7 @@ $(curmod_prefix)Undocumented.st4{T, N} <: $(curmod_prefix)Undocumented.at0{T, N} @test docstrings_equal(@doc(Undocumented.st4), doc"$doc_str") doc_str = Markdown.parse(""" -No documentation found for private symbol. +No documentation found for private binding `$(curmod_prefix)Undocumented.st5`. # Summary @@ -1119,7 +1121,7 @@ $(curmod_prefix)Undocumented.st5{T>:Int64, N} <: $(curmod_prefix)Undocumented.at @test docstrings_equal(@doc(Undocumented.st5), doc"$doc_str") doc_str = Markdown.parse(""" -No documentation found for private symbol. +No documentation found for private binding `$(curmod_prefix)Undocumented.mt6`. # Summary @@ -1140,7 +1142,7 @@ $(curmod_prefix)Undocumented.mt6{T<:Integer, N} <: $(curmod_prefix)Undocumented. @test docstrings_equal(@doc(Undocumented.mt6), doc"$doc_str") doc_str = Markdown.parse(""" -No documentation found for private symbol. +No documentation found for private binding `$(curmod_prefix)Undocumented.ut7`. # Summary @@ -1154,7 +1156,7 @@ No documentation found for private symbol. @test docstrings_equal(@doc(Undocumented.ut7), doc"$doc_str") doc_str = Markdown.parse(""" -No documentation found for private symbol. +No documentation found for private binding `$(curmod_prefix)Undocumented.ut8`. # Summary @@ -1170,7 +1172,7 @@ No documentation found for private symbol. @test docstrings_equal(@doc(Undocumented.ut8), doc"$doc_str") doc_str = Markdown.parse(""" -No documentation found for private symbol. +No documentation found for private binding `$(curmod_prefix)Undocumented.ut9`. # Summary @@ -1189,7 +1191,7 @@ let d = @doc(Undocumented.f) io = IOBuffer() show(io, MIME"text/markdown"(), d) @test startswith(String(take!(io)),""" - No documentation found for private symbol. + No documentation found for private binding `$(curmod_prefix)Undocumented.f`. `$(curmod_prefix)Undocumented.f` is a `Function`. """) @@ -1199,7 +1201,7 @@ let d = @doc(Undocumented.undocumented) io = IOBuffer() show(io, MIME"text/markdown"(), d) @test startswith(String(take!(io)), """ - No documentation found for private symbol. + No documentation found for private binding `$(curmod_prefix)Undocumented.undocumented`. `$(curmod_prefix)Undocumented.undocumented` is a `Function`. """) From 755d7ece07d78f681c8d3cb07be489bb7104c847 Mon Sep 17 00:00:00 2001 From: DilumAluthgeBot <43731525+DilumAluthgeBot@users.noreply.github.com> Date: Mon, 20 Jan 2025 18:10:10 -0500 Subject: [PATCH 02/56] =?UTF-8?q?=F0=9F=A4=96=20[master]=20Bump=20the=20SH?= =?UTF-8?q?A=20stdlib=20from=208fa221d=20to=204451e13=20(#57110)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../SHA-4451e1362e425bcbc1652ecf55fc0e525b18fb63.tar.gz/md5 | 1 + .../SHA-4451e1362e425bcbc1652ecf55fc0e525b18fb63.tar.gz/sha512 | 1 + .../SHA-8fa221ddc8f3b418d9929084f1644f4c32c9a27e.tar.gz/md5 | 1 - .../SHA-8fa221ddc8f3b418d9929084f1644f4c32c9a27e.tar.gz/sha512 | 1 - stdlib/SHA.version | 2 +- 5 files changed, 3 insertions(+), 3 deletions(-) create mode 100644 deps/checksums/SHA-4451e1362e425bcbc1652ecf55fc0e525b18fb63.tar.gz/md5 create mode 100644 deps/checksums/SHA-4451e1362e425bcbc1652ecf55fc0e525b18fb63.tar.gz/sha512 delete mode 100644 deps/checksums/SHA-8fa221ddc8f3b418d9929084f1644f4c32c9a27e.tar.gz/md5 delete mode 100644 deps/checksums/SHA-8fa221ddc8f3b418d9929084f1644f4c32c9a27e.tar.gz/sha512 diff --git a/deps/checksums/SHA-4451e1362e425bcbc1652ecf55fc0e525b18fb63.tar.gz/md5 b/deps/checksums/SHA-4451e1362e425bcbc1652ecf55fc0e525b18fb63.tar.gz/md5 new file mode 100644 index 0000000000000..cbd2acb2c6f66 --- /dev/null +++ b/deps/checksums/SHA-4451e1362e425bcbc1652ecf55fc0e525b18fb63.tar.gz/md5 @@ -0,0 +1 @@ +7b511b7dab411685206d0d90cc1fb56e diff --git a/deps/checksums/SHA-4451e1362e425bcbc1652ecf55fc0e525b18fb63.tar.gz/sha512 b/deps/checksums/SHA-4451e1362e425bcbc1652ecf55fc0e525b18fb63.tar.gz/sha512 new file mode 100644 index 0000000000000..5201bbdcc40f9 --- /dev/null +++ b/deps/checksums/SHA-4451e1362e425bcbc1652ecf55fc0e525b18fb63.tar.gz/sha512 @@ -0,0 +1 @@ +960367406d80e46e8e742bcb0f7f0e4b089b664c2321ca82953eb760b325693ae57f431d891ccf56c3ab9146bc29682d2d1767bc635f4dbe6dd4d80030a42487 diff --git a/deps/checksums/SHA-8fa221ddc8f3b418d9929084f1644f4c32c9a27e.tar.gz/md5 b/deps/checksums/SHA-8fa221ddc8f3b418d9929084f1644f4c32c9a27e.tar.gz/md5 deleted file mode 100644 index 52e05f5e427ae..0000000000000 --- a/deps/checksums/SHA-8fa221ddc8f3b418d9929084f1644f4c32c9a27e.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -e52615827242aae56422a4f73a8c6878 diff --git a/deps/checksums/SHA-8fa221ddc8f3b418d9929084f1644f4c32c9a27e.tar.gz/sha512 b/deps/checksums/SHA-8fa221ddc8f3b418d9929084f1644f4c32c9a27e.tar.gz/sha512 deleted file mode 100644 index e6b8446587554..0000000000000 --- a/deps/checksums/SHA-8fa221ddc8f3b418d9929084f1644f4c32c9a27e.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -7b1df257616aaa9067f822a88dddf52bc10f9f61e3a0728e33e595455bd7167e680c50371c41cb25f8c8a9fb9cf40225847df1523a6c6f3571a471f7163f563c diff --git a/stdlib/SHA.version b/stdlib/SHA.version index 4b33964a6dcdb..a5d4372d5798b 100644 --- a/stdlib/SHA.version +++ b/stdlib/SHA.version @@ -1,4 +1,4 @@ SHA_BRANCH = master -SHA_SHA1 = 8fa221ddc8f3b418d9929084f1644f4c32c9a27e +SHA_SHA1 = 4451e1362e425bcbc1652ecf55fc0e525b18fb63 SHA_GIT_URL := https://github.com/JuliaCrypto/SHA.jl.git SHA_TAR_URL = https://api.github.com/repos/JuliaCrypto/SHA.jl/tarball/$1 From 2e6ffbccab206d5b3963f4396ba0d541cc2394d1 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Mon, 20 Jan 2025 21:23:42 -0500 Subject: [PATCH 03/56] Capture/silence some noisy test warnings (#57111) --- Compiler/test/codegen.jl | 2 +- test/ambiguous.jl | 2 +- test/boundscheck_exec.jl | 7 +++---- test/channel_threadpool.jl | 16 +++++++--------- test/spawn.jl | 2 +- test/syntax.jl | 3 ++- 6 files changed, 15 insertions(+), 17 deletions(-) diff --git a/Compiler/test/codegen.jl b/Compiler/test/codegen.jl index 9b92f560c64fc..ff61ac719c59e 100644 --- a/Compiler/test/codegen.jl +++ b/Compiler/test/codegen.jl @@ -1031,7 +1031,7 @@ end # Make sure that code that has unbound sparams works #https://github.com/JuliaLang/julia/issues/56739 -f56739(a) where {T} = a +@test_warn r"declares type variable T but does not use it" @eval f56739(a) where {T} = a @test f56739(1) == 1 g56739(x) = @noinline f56739(x) diff --git a/test/ambiguous.jl b/test/ambiguous.jl index 43ec1aab0557d..5f859e773f5d2 100644 --- a/test/ambiguous.jl +++ b/test/ambiguous.jl @@ -97,7 +97,7 @@ ambig(x::Union{Char, Int16}) = 's' # Automatic detection of ambiguities -const allowed_undefineds = Set([]) +const allowed_undefineds = Set([GlobalRef(Base, :active_repl)]) let Distributed = get(Base.loaded_modules, Base.PkgId(Base.UUID("8ba89e20-285c-5b6f-9357-94700520ee1b"), "Distributed"), diff --git a/test/boundscheck_exec.jl b/test/boundscheck_exec.jl index 85df1d64017b4..3b2f853999229 100644 --- a/test/boundscheck_exec.jl +++ b/test/boundscheck_exec.jl @@ -298,7 +298,7 @@ end end |> only === Type{Int} if bc_opt == bc_default -@testset "Array/Memory escape analysis" begin + # Array/Memory escape analysis function no_allocate(T::Type{<:Union{Memory, Vector}}) v = T(undef, 2) v[1] = 2 @@ -308,8 +308,8 @@ if bc_opt == bc_default function test_alloc(::Type{T}; broken=false) where T @test (@allocated no_allocate(T)) == 0 broken=broken end - @testset "$T" for T in [Memory, Vector] - @testset "$ET" for ET in [Int, Float32, Union{Int, Float64}] + for T in [Memory, Vector] + for ET in [Int, Float32, Union{Int, Float64}] no_allocate(T{ET}) #compile # allocations aren't removed for Union eltypes which they theoretically could be eventually test_alloc(T{ET}, broken=(ET==Union{Int, Float64})) @@ -345,6 +345,5 @@ if bc_opt == bc_default no_alias_prove(1) @test_broken (@allocated no_alias_prove(5)) == 0 end -end end diff --git a/test/channel_threadpool.jl b/test/channel_threadpool.jl index 4509604087fa8..54c2fc0f83e09 100644 --- a/test/channel_threadpool.jl +++ b/test/channel_threadpool.jl @@ -3,12 +3,10 @@ using Test using Base.Threads -@testset "Task threadpools" begin - c = Channel{Symbol}() do c; put!(c, threadpool(current_task())); end - @test take!(c) === threadpool(current_task()) - c = Channel{Symbol}(spawn = true) do c; put!(c, threadpool(current_task())); end - @test take!(c) === :default - c = Channel{Symbol}(threadpool = :interactive) do c; put!(c, threadpool(current_task())); end - @test take!(c) === :interactive - @test_throws ArgumentError Channel{Symbol}(threadpool = :foo) do c; put!(c, :foo); end -end +c = Channel{Symbol}() do c; put!(c, threadpool(current_task())); end +@test take!(c) === threadpool(current_task()) +c = Channel{Symbol}(spawn = true) do c; put!(c, threadpool(current_task())); end +@test take!(c) === :default +c = Channel{Symbol}(threadpool = :interactive) do c; put!(c, threadpool(current_task())); end +@test take!(c) === :interactive +@test_throws ArgumentError Channel{Symbol}(threadpool = :foo) do c; put!(c, :foo); end diff --git a/test/spawn.jl b/test/spawn.jl index c1802ba1f74da..0356cf9871424 100644 --- a/test/spawn.jl +++ b/test/spawn.jl @@ -78,7 +78,7 @@ out = read(`$echocmd hello` & `$echocmd world`, String) @test occursin("hello", out) @test read(pipeline(`$echocmd hello` & `$echocmd world`, sortcmd), String) == "hello\nworld\n" -@test (run(`$printfcmd " \033[34m[stdio passthrough ok]\033[0m\n"`); true) +@test_warn r"[stdio passthrough ok]" run(pipeline(`$printfcmd " \033[34m[stdio passthrough ok]\033[0m\n"`, stdout=stderr, stderr=stderr)) # Test for SIGPIPE being a failure condition @test_throws ProcessFailedException run(pipeline(yescmd, `head`, devnull)) diff --git a/test/syntax.jl b/test/syntax.jl index 9fd0204821eab..bdf9579b134eb 100644 --- a/test/syntax.jl +++ b/test/syntax.jl @@ -3971,11 +3971,12 @@ end # Module Replacement module ReplacementContainer + using Test module ReplaceMe const x = 1 end const Old = ReplaceMe - module ReplaceMe + @test_warn r"WARNING: replacing module ReplaceMe" @eval module ReplaceMe const x = 2 end end From 90d346f9b9760584d94a3f16a76f56acb45cdf01 Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Tue, 21 Jan 2025 21:15:44 +0900 Subject: [PATCH 04/56] fix `help?> StructType.field` command (#57107) `help?> StructType.field` currently errors when `StructType` doesn't have any field documentation at all. This commit adds a proper guard against such cases. --- stdlib/REPL/src/docview.jl | 18 +++++++++++------- stdlib/REPL/test/docview.jl | 3 +++ 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/stdlib/REPL/src/docview.jl b/stdlib/REPL/src/docview.jl index e0710d89a67e2..313994505b3ee 100644 --- a/stdlib/REPL/src/docview.jl +++ b/stdlib/REPL/src/docview.jl @@ -662,13 +662,17 @@ function fielddoc(binding::Binding, field::Symbol) for mod in modules dict = meta(mod; autoinit=false) isnothing(dict) && continue - if haskey(dict, binding) - multidoc = dict[binding] - if haskey(multidoc.docs, Union{}) - fields = multidoc.docs[Union{}].data[:fields] - if haskey(fields, field) - doc = fields[field] - return isa(doc, Markdown.MD) ? doc : Markdown.parse(doc) + multidoc = get(dict, binding, nothing) + if multidoc !== nothing + structdoc = get(multidoc.docs, Union{}, nothing) + if structdoc !== nothing + fieldsdoc = get(structdoc.data, :fields, nothing) + if fieldsdoc !== nothing + fielddoc = get(fieldsdoc, field, nothing) + if fielddoc !== nothing + return isa(fielddoc, Markdown.MD) ? + fielddoc : Markdown.parse(fielddoc) + end end end end diff --git a/stdlib/REPL/test/docview.jl b/stdlib/REPL/test/docview.jl index 02f1dc8238f04..c81715ad69921 100644 --- a/stdlib/REPL/test/docview.jl +++ b/stdlib/REPL/test/docview.jl @@ -92,6 +92,9 @@ end @test endswith(get_help_standard("StructWithOneField.not_a_field"), "StructWithOneField` has field `field1`.\n") @test endswith(get_help_standard("StructWithTwoFields.not_a_field"), "StructWithTwoFields` has fields `field1`, and `field2`.\n") @test endswith(get_help_standard("StructWithThreeFields.not_a_field"), "StructWithThreeFields` has fields `field1`, `field2`, and `field3`.\n") + + # Shouldn't error if the struct doesn't have any field documentations at all. + @test endswith(get_help_standard("Int.not_a_field"), "`$Int` has no fields.\n") end module InternalWarningsTests From ff5f66c655277636e7e27a7927611af2805d4a2e Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Tue, 21 Jan 2025 10:01:35 -0500 Subject: [PATCH 05/56] codegen: export all ABIs that we refer to in codegen (#57083) Since codegen may refer to these, it helps to have the linker able to resolve them, if that case ever happens. Fixes #56701 --- src/gf.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/gf.c b/src/gf.c index ba28edfbeeff7..ea25ed2eae4ff 100644 --- a/src/gf.c +++ b/src/gf.c @@ -3034,19 +3034,19 @@ jl_code_instance_t *jl_compile_method_internal(jl_method_instance_t *mi, size_t return codeinst; } -jl_value_t *jl_fptr_const_return(jl_value_t *f, jl_value_t **args, uint32_t nargs, jl_code_instance_t *m) +JL_DLLEXPORT jl_value_t *jl_fptr_const_return(jl_value_t *f, jl_value_t **args, uint32_t nargs, jl_code_instance_t *m) { return m->rettype_const; } -jl_value_t *jl_fptr_args(jl_value_t *f, jl_value_t **args, uint32_t nargs, jl_code_instance_t *m) +JL_DLLEXPORT jl_value_t *jl_fptr_args(jl_value_t *f, jl_value_t **args, uint32_t nargs, jl_code_instance_t *m) { jl_fptr_args_t invoke = jl_atomic_load_relaxed(&m->specptr.fptr1); assert(invoke && "Forgot to set specptr for jl_fptr_args!"); return invoke(f, args, nargs); } -jl_value_t *jl_fptr_sparam(jl_value_t *f, jl_value_t **args, uint32_t nargs, jl_code_instance_t *m) +JL_DLLEXPORT jl_value_t *jl_fptr_sparam(jl_value_t *f, jl_value_t **args, uint32_t nargs, jl_code_instance_t *m) { jl_svec_t *sparams = jl_get_ci_mi(m)->sparam_vals; assert(sparams != jl_emptysvec); From 7a1a6f32ec89745844b93cfabe2fe48fcac03ded Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Tue, 21 Jan 2025 10:03:00 -0500 Subject: [PATCH 06/56] codegen: use correct rettype ABI for aotcompile (#57082) This only really can differ in aotcompile, so it was hard to notice that that the attempted code simplification in db25494f62 wasn't correct and could lead to ABI mismatches at runtime. Fixes #57018 --- src/aotcompile.cpp | 2 +- src/codegen.cpp | 17 ++++++++--------- src/jitlayers.h | 3 ++- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/aotcompile.cpp b/src/aotcompile.cpp index 0235758979cd1..5524518da46fa 100644 --- a/src/aotcompile.cpp +++ b/src/aotcompile.cpp @@ -2223,7 +2223,7 @@ void jl_get_llvmf_defn_impl(jl_llvmf_dump_t* dump, jl_method_instance_t *mi, jl_ output.imaging_mode = jl_options.image_codegen; output.temporary_roots = jl_alloc_array_1d(jl_array_any_type, 0); JL_GC_PUSH1(&output.temporary_roots); - auto decls = jl_emit_code(m, mi, src, NULL, output); + auto decls = jl_emit_code(m, mi, src, mi->specTypes, src->rettype, output); output.temporary_roots = nullptr; JL_GC_POP(); // GC the global_targets array contents now since reflection doesn't need it diff --git a/src/codegen.cpp b/src/codegen.cpp index eefc675bdd665..7cfcc52db29c7 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -4381,7 +4381,7 @@ static jl_llvm_functions_t jl_method_instance_t *lam, jl_code_info_t *src, jl_value_t *abi, - jl_value_t *rettype, + jl_value_t *jlrettype, jl_codegen_params_t ¶ms); static void emit_hasnofield_error_ifnot(jl_codectx_t &ctx, Value *ok, jl_datatype_t *type, jl_cgval_t name); @@ -5533,12 +5533,12 @@ static jl_value_t *get_ci_abi(jl_code_instance_t *ci) return jl_get_ci_mi(ci)->specTypes; } -static jl_cgval_t emit_call_specfun_other(jl_codectx_t &ctx, jl_code_instance_t *ci, jl_value_t *jlretty, StringRef specFunctionObject, jl_code_instance_t *fromexternal, +static jl_cgval_t emit_call_specfun_other(jl_codectx_t &ctx, jl_code_instance_t *ci, StringRef specFunctionObject, jl_code_instance_t *fromexternal, ArrayRef argv, size_t nargs, jl_returninfo_t::CallingConv *cc, unsigned *return_roots, jl_value_t *inferred_retty, Value *age_ok) { jl_method_instance_t *mi = jl_get_ci_mi(ci); bool is_opaque_closure = jl_is_method(mi->def.value) && mi->def.method->is_for_opaque_closure; - return emit_call_specfun_other(ctx, is_opaque_closure, get_ci_abi(ci), jlretty, NULL, + return emit_call_specfun_other(ctx, is_opaque_closure, get_ci_abi(ci), ci->rettype, NULL, specFunctionObject, fromexternal, argv, nargs, cc, return_roots, inferred_retty, age_ok); } @@ -5688,7 +5688,7 @@ static jl_cgval_t emit_invoke(jl_codectx_t &ctx, const jl_cgval_t &lival, ArrayR jl_returninfo_t::CallingConv cc = jl_returninfo_t::CallingConv::Boxed; unsigned return_roots = 0; if (specsig) - result = emit_call_specfun_other(ctx, codeinst, codeinst->rettype, protoname, external ? codeinst : nullptr, argv, nargs, &cc, &return_roots, rt, age_ok); + result = emit_call_specfun_other(ctx, codeinst, protoname, external ? codeinst : nullptr, argv, nargs, &cc, &return_roots, rt, age_ok); else result = emit_call_specfun_boxed(ctx, codeinst->rettype, protoname, external ? codeinst : nullptr, argv, nargs, rt, age_ok); if (need_to_emit) { @@ -10029,7 +10029,8 @@ jl_llvm_functions_t jl_emit_code( orc::ThreadSafeModule &m, jl_method_instance_t *li, jl_code_info_t *src, - jl_value_t *abi, + jl_value_t *abi_at, + jl_value_t *abi_rt, jl_codegen_params_t ¶ms) { JL_TIMING(CODEGEN, CODEGEN_LLVM); @@ -10038,10 +10039,8 @@ jl_llvm_functions_t jl_emit_code( assert((params.params == &jl_default_cgparams /* fast path */ || !params.cache || compare_cgparams(params.params, &jl_default_cgparams)) && "functions compiled with custom codegen params must not be cached"); - if (!abi) - abi = li->specTypes; JL_TRY { - decls = emit_function(m, li, src, abi, src->rettype, params); + decls = emit_function(m, li, src, abi_at, abi_rt, params); auto stream = *jl_ExecutionEngine->get_dump_emitted_mi_name_stream(); if (stream) { jl_printf(stream, "%s\t", decls.specFunctionObject.c_str()); @@ -10112,7 +10111,7 @@ jl_llvm_functions_t jl_emit_codeinst( return jl_llvm_functions_t(); // user error } //assert(jl_egal((jl_value_t*)jl_atomic_load_relaxed(&codeinst->debuginfo), (jl_value_t*)src->debuginfo) && "trying to generate code for a codeinst for an incompatible src"); - jl_llvm_functions_t decls = jl_emit_code(m, jl_get_ci_mi(codeinst), src, get_ci_abi(codeinst), params); + jl_llvm_functions_t decls = jl_emit_code(m, jl_get_ci_mi(codeinst), src, get_ci_abi(codeinst), codeinst->rettype, params); return decls; } diff --git a/src/jitlayers.h b/src/jitlayers.h index 7198c9b2f0210..4637670ec588c 100644 --- a/src/jitlayers.h +++ b/src/jitlayers.h @@ -278,7 +278,8 @@ jl_llvm_functions_t jl_emit_code( orc::ThreadSafeModule &M, jl_method_instance_t *mi, jl_code_info_t *src, - jl_value_t *abi, + jl_value_t *abi_at, + jl_value_t *abi_rt, jl_codegen_params_t ¶ms); jl_llvm_functions_t jl_emit_codeinst( From 4e13e0e449aee71a5ad7f335fbc8812d28c51cf2 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Tue, 21 Jan 2025 10:04:45 -0500 Subject: [PATCH 07/56] inference: fix lattice for unusual InterConditional return and Const Bool representations (#57080) Fixes #54886 Rule introduced by 55cee67 --- Compiler/src/abstractinterpretation.jl | 11 ++++++++++- Compiler/src/typelattice.jl | 7 +++++++ Compiler/test/inference.jl | 20 ++++++++++++++++++++ 3 files changed, 37 insertions(+), 1 deletion(-) diff --git a/Compiler/src/abstractinterpretation.jl b/Compiler/src/abstractinterpretation.jl index 59510dbfbb65a..a6c75ed411082 100644 --- a/Compiler/src/abstractinterpretation.jl +++ b/Compiler/src/abstractinterpretation.jl @@ -3785,7 +3785,7 @@ function update_bestguess!(interp::AbstractInterpreter, frame::InferenceState, slottypes = frame.slottypes rt = widenreturn(rt, BestguessInfo(interp, bestguess, nargs, slottypes, currstate)) # narrow representation of bestguess slightly to prepare for tmerge with rt - if rt isa InterConditional && bestguess isa Const + if rt isa InterConditional && bestguess isa Const && bestguess.val isa Bool slot_id = rt.slot old_id_type = widenconditional(slottypes[slot_id]) if bestguess.val === true && rt.elsetype !== Bottom @@ -3793,6 +3793,15 @@ function update_bestguess!(interp::AbstractInterpreter, frame::InferenceState, elseif bestguess.val === false && rt.thentype !== Bottom bestguess = InterConditional(slot_id, Bottom, old_id_type) end + # or narrow representation of rt slightly to prepare for tmerge with bestguess + elseif bestguess isa InterConditional && rt isa Const && rt.val isa Bool + slot_id = bestguess.slot + old_id_type = widenconditional(slottypes[slot_id]) + if rt.val === true && bestguess.elsetype !== Bottom + rt = InterConditional(slot_id, old_id_type, Bottom) + elseif rt.val === false && bestguess.thentype !== Bottom + rt = InterConditional(slot_id, Bottom, old_id_type) + end end # copy limitations to return value if !isempty(frame.pclimitations) diff --git a/Compiler/src/typelattice.jl b/Compiler/src/typelattice.jl index bd0d24167b75a..6f7612b836c89 100644 --- a/Compiler/src/typelattice.jl +++ b/Compiler/src/typelattice.jl @@ -395,6 +395,13 @@ end end a = Bool elseif isa(b, ConditionalT) + if isa(a, Const) && isa(a.val, Bool) + if (a.val === true && b.thentype === Any && b.elsetype === Bottom) || + (a.val === false && b.elsetype === Any && b.thentype === Bottom) + # this Conditional contains distinctly no lattice information, and is simply an alternative representation of the Const Bool used for internal tracking purposes + return true + end + end return false end return ⊑(widenlattice(lattice), a, b) diff --git a/Compiler/test/inference.jl b/Compiler/test/inference.jl index 21d29c376bb27..b74460203e66e 100644 --- a/Compiler/test/inference.jl +++ b/Compiler/test/inference.jl @@ -2734,6 +2734,26 @@ vacond(cnd, va...) = cnd ? va : 0 vacond(isa(x, Tuple{Int,Int}), x, x) end |> only == Union{Int,Tuple{Any,Any}} +let A = Core.Const(true) + B = Core.InterConditional(2, Tuple, Union{}) + C = Core.InterConditional(2, Any, Union{}) + L = ipo_lattice(Compiler.NativeInterpreter()) + @test !⊑(L, A, B) + @test ⊑(L, B, A) + @test tmerge(L, A, B) == C + @test ⊑(L, A, C) +end +function tail_is_ntuple((@nospecialize t::Tuple)) + if unknown + t isa Tuple + else + tail_is_ntuple(t) + end +end +tail_is_ntuple_val((@nospecialize t::Tuple)) = Val(tail_is_ntuple(t)) +@test Base.return_types(tail_is_ntuple, (Tuple,)) |> only === Bool +@test Base.return_types(tail_is_ntuple_val, (Tuple,)) |> only === Val{true} + # https://github.com/JuliaLang/julia/issues/47435 is_closed_ex(e::InvalidStateException) = true is_closed_ex(e) = false From 323ca86cb5ff614b713c3995ea11b7755b74190c Mon Sep 17 00:00:00 2001 From: Chengyu Han Date: Wed, 22 Jan 2025 00:15:50 +0800 Subject: [PATCH 08/56] base: add `Sys.detectwsl()` (#57069) Close #36425, fix #36354 ## How to detect WSL? There are a number of ways that can be used to detect WSL environments, but each can have false positives. We finally chose to use the same method as Snapd to detect WSL. Because Windows installs Ubuntu LTS as WSL by default. So we assume that Snapd's detection method will work for most users. - Ubuntu/Snapd: https://github.com/canonical/snapd/blob/03a578a5dff26467dcc80580fcd4720a486185a5/release/release.go#L151-L172 - microsoft/WSL#423 - microsoft/WSL#4071 - https://superuser.com/q/1749781/1460597 ## Known limitations - this is a runtime test, and thus cannot meaningfully be used in `@static if` constructs. - Linux users can create their own - `/proc/sys/fs/binfmt_misc/WSLInterop` file - or `/run/WSL/` folder to pretend to be a WSL environment. --- - I've tested this under: Ubuntu 22.04.5 LTS (default/Offical) and alpine-release-3.17.0 (win store) - [x] Add compat, NEWS, tests - [x] Take a look at different detect methods, figure out which one is more robust --------- Co-authored-by: Gautam Mishra Co-authored-by: Max Horn <241512+fingolfin@users.noreply.github.com> Co-authored-by: Chengyu Han Co-authored-by: Alex Arslan --- NEWS.md | 1 + base/sysinfo.jl | 24 +++++++++++++++++++++++- test/osutils.jl | 5 +++++ test/path.jl | 2 +- 4 files changed, 30 insertions(+), 2 deletions(-) diff --git a/NEWS.md b/NEWS.md index 6c378c8186007..71014e1e57695 100644 --- a/NEWS.md +++ b/NEWS.md @@ -97,6 +97,7 @@ New library functions * `uuid7()` creates an RFC 9652 compliant UUID with version 7 ([#54834]). * `insertdims(array; dims)` allows to insert singleton dimensions into an array which is the inverse operation to `dropdims`. ([#45793]) * The new `Fix` type is a generalization of `Fix1/Fix2` for fixing a single argument ([#54653]). +* `Sys.detectwsl()` allows to testing if Julia is running inside WSL at runtime. ([#57069]) New library features -------------------- diff --git a/base/sysinfo.jl b/base/sysinfo.jl index 7dab313cf4f57..c96c318ec053b 100644 --- a/base/sysinfo.jl +++ b/base/sysinfo.jl @@ -36,7 +36,8 @@ export BINDIR, isreadable, iswritable, username, - which + which, + detectwsl import ..Base: show @@ -532,6 +533,27 @@ including e.g. a WebAssembly JavaScript embedding in a web browser. """ isjsvm(os::Symbol) = (os === :Emscripten) +""" + Sys.detectwsl() + +Runtime predicate for testing if Julia is running inside +Windows Subsystem for Linux (WSL). + +!!! note + Unlike `Sys.iswindows`, `Sys.islinux` etc., this is a runtime test, and thus + cannot meaningfully be used in `@static if` constructs. + +!!! compat "Julia 1.12" + This function requires at least Julia 1.12. +""" +function detectwsl() + # We use the same approach as canonical/snapd do to detect WSL + islinux() && ( + isfile("/proc/sys/fs/binfmt_misc/WSLInterop") + || isdir("/run/WSL") + ) +end + for f in (:isunix, :islinux, :isbsd, :isapple, :iswindows, :isfreebsd, :isopenbsd, :isnetbsd, :isdragonfly, :isjsvm) @eval $f() = $(getfield(@__MODULE__, f)(KERNEL)) end diff --git a/test/osutils.jl b/test/osutils.jl index 5e72675279cbc..9eb708b670298 100644 --- a/test/osutils.jl +++ b/test/osutils.jl @@ -29,6 +29,11 @@ using Libdl else @test Sys.windows_version() >= v"1.0.0-" end + + # TODO: When we have a WSL CI, add a new test here `@test detectwsl()` + if !Sys.islinux() + @test !Sys.detectwsl() + end end @testset "@static" begin diff --git a/test/path.jl b/test/path.jl index 2515d765d8ca9..a2824a24c8bce 100644 --- a/test/path.jl +++ b/test/path.jl @@ -314,7 +314,7 @@ @testset "uripath" begin host = if Sys.iswindows() "" - elseif ispath("/proc/sys/fs/binfmt_misc/WSLInterop") + elseif Sys.detectwsl() distro = get(ENV, "WSL_DISTRO_NAME", "") # See "wsl%24/$distro" # See and else From eb9f24c8ceb964464939775e164366939755393c Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Tue, 21 Jan 2025 13:12:05 -0500 Subject: [PATCH 09/56] inference: ensure inferring reachable code methods (#57088) PR #51317 was a bit over-eager about inferring inferring unreachable code methods. Filter out the Vararg case, since that can be handled by simply removing it instead of discarding the whole call. Fixes #56628 --- Compiler/src/abstractinterpretation.jl | 4 ++- Compiler/src/ssair/inlining.jl | 1 + Compiler/src/tfuncs.jl | 36 ++++++++++++++------------ Compiler/src/typeutils.jl | 7 ++++- Compiler/test/inference.jl | 6 +++++ 5 files changed, 36 insertions(+), 18 deletions(-) diff --git a/Compiler/src/abstractinterpretation.jl b/Compiler/src/abstractinterpretation.jl index a6c75ed411082..300feeae53e9a 100644 --- a/Compiler/src/abstractinterpretation.jl +++ b/Compiler/src/abstractinterpretation.jl @@ -368,6 +368,7 @@ function find_union_split_method_matches(interp::AbstractInterpreter, argtypes:: for i in 1:length(split_argtypes) arg_n = split_argtypes[i]::Vector{Any} sig_n = argtypes_to_type(arg_n) + sig_n === Bottom && continue mt = ccall(:jl_method_table_for, Any, (Any,), sig_n) mt === nothing && return FailedMethodMatch("Could not identify method table for call") mt = mt::MethodTable @@ -614,7 +615,7 @@ function abstract_call_method(interp::AbstractInterpreter, sigtuple = unwrap_unionall(sig) sigtuple isa DataType || return Future(MethodCallResult(Any, Any, Effects(), nothing, false, false)) - all(@nospecialize(x) -> valid_as_lattice(unwrapva(x), true), sigtuple.parameters) || + all(@nospecialize(x) -> isvarargtype(x) || valid_as_lattice(x, true), sigtuple.parameters) || return Future(MethodCallResult(Union{}, Any, EFFECTS_THROWS, nothing, false, false)) # catch bad type intersections early if is_nospecializeinfer(method) @@ -2840,6 +2841,7 @@ function abstract_call_unknown(interp::AbstractInterpreter, @nospecialize(ft), end # non-constant function, but the number of arguments is known and the `f` is not a builtin or intrinsic atype = argtypes_to_type(arginfo.argtypes) + atype === Bottom && return Future(CallMeta(Union{}, Union{}, EFFECTS_THROWS, NoCallInfo())) # accidentally unreachable return abstract_call_gf_by_type(interp, nothing, arginfo, si, atype, sv, max_methods)::Future end diff --git a/Compiler/src/ssair/inlining.jl b/Compiler/src/ssair/inlining.jl index 0c0d14bf8f25a..120b891f09a9f 100644 --- a/Compiler/src/ssair/inlining.jl +++ b/Compiler/src/ssair/inlining.jl @@ -1399,6 +1399,7 @@ function handle_call!(todo::Vector{Pair{Int,Any}}, cases === nothing && return nothing cases, handled_all_cases, fully_covered, joint_effects = cases atype = argtypes_to_type(sig.argtypes) + atype === Union{} && return nothing # accidentally actually unreachable handle_cases!(todo, ir, idx, stmt, atype, cases, handled_all_cases, fully_covered, joint_effects) end diff --git a/Compiler/src/tfuncs.jl b/Compiler/src/tfuncs.jl index cfb865b06e9e5..fe21f6381e8cd 100644 --- a/Compiler/src/tfuncs.jl +++ b/Compiler/src/tfuncs.jl @@ -3016,24 +3016,28 @@ function abstract_applicable(interp::AbstractInterpreter, argtypes::Vector{Any}, isvarargtype(argtypes[2]) && return Future(CallMeta(Bool, ArgumentError, EFFECTS_THROWS, NoCallInfo())) argtypes = argtypes[2:end] atype = argtypes_to_type(argtypes) - matches = find_method_matches(interp, argtypes, atype; max_methods) - info = NoCallInfo() - if isa(matches, FailedMethodMatch) - rt = Bool # too many matches to analyze + if atype === Union{} + rt = Union{} # accidentally unreachable code else - (; valid_worlds, applicable) = matches - update_valid_age!(sv, valid_worlds) - napplicable = length(applicable) - if napplicable == 0 - rt = Const(false) # never any matches - elseif !fully_covering(matches) || any_ambig(matches) - # Account for the fact that we may encounter a MethodError with a non-covered or ambiguous signature. - rt = Bool + matches = find_method_matches(interp, argtypes, atype; max_methods) + info = NoCallInfo() + if isa(matches, FailedMethodMatch) + rt = Bool # too many matches to analyze else - rt = Const(true) # has applicable matches - end - if rt !== Bool - info = VirtualMethodMatchInfo(matches.info) + (; valid_worlds, applicable) = matches + update_valid_age!(sv, valid_worlds) + napplicable = length(applicable) + if napplicable == 0 + rt = Const(false) # never any matches + elseif !fully_covering(matches) || any_ambig(matches) + # Account for the fact that we may encounter a MethodError with a non-covered or ambiguous signature. + rt = Bool + else + rt = Const(true) # has applicable matches + end + if rt !== Bool + info = VirtualMethodMatchInfo(matches.info) + end end end return Future(CallMeta(rt, Union{}, EFFECTS_TOTAL, info)) diff --git a/Compiler/src/typeutils.jl b/Compiler/src/typeutils.jl index 5175e00612270..d588a9aee1a6c 100644 --- a/Compiler/src/typeutils.jl +++ b/Compiler/src/typeutils.jl @@ -54,7 +54,12 @@ has_extended_info(@nospecialize x) = (!isa(x, Type) && !isvarargtype(x)) || isTy # certain combinations of `a` and `b` where one/both isa/are `Union`/`UnionAll` type(s)s. isnotbrokensubtype(@nospecialize(a), @nospecialize(b)) = (!iskindtype(b) || !isType(a) || hasuniquerep(a.parameters[1]) || b <: a) -argtypes_to_type(argtypes::Array{Any,1}) = Tuple{anymap(@nospecialize(a) -> isvarargtype(a) ? a : widenconst(a), argtypes)...} +function argtypes_to_type(argtypes::Array{Any,1}) + argtypes = anymap(@nospecialize(a) -> isvarargtype(a) ? a : widenconst(a), argtypes) + filter!(@nospecialize(x) -> !isvarargtype(x) || valid_as_lattice(unwrapva(x), true), argtypes) + all(@nospecialize(x) -> isvarargtype(x) || valid_as_lattice(x, true), argtypes) || return Bottom + return Tuple{argtypes...} +end function isknownlength(t::DataType) isvatuple(t) || return true diff --git a/Compiler/test/inference.jl b/Compiler/test/inference.jl index b74460203e66e..d4ea990e7d148 100644 --- a/Compiler/test/inference.jl +++ b/Compiler/test/inference.jl @@ -6182,3 +6182,9 @@ end <: Any end return out end == Union{Float64,DomainError} + +# issue #56628 +@test Compiler.argtypes_to_type(Any[ Int, UnitRange{Int}, Vararg{Pair{Any, Union{}}} ]) === Tuple{Int, UnitRange{Int}} +@test Compiler.argtypes_to_type(Any[ Int, UnitRange{Int}, Vararg{Pair{Any, Union{}}}, Float64 ]) === Tuple{Int, UnitRange{Int}, Float64} +@test Compiler.argtypes_to_type(Any[ Int, UnitRange{Int}, Vararg{Pair{Any, Union{}}}, Float64, Memory{2} ]) === Union{} +@test Base.return_types(Tuple{Tuple{Int, Vararg{Pair{Any, Union{}}}}},) do x; Returns(true)(x...); end |> only === Bool From cb553897e82f7bc6a51d30c1db531f54abae1a8b Mon Sep 17 00:00:00 2001 From: Oscar Smith Date: Tue, 21 Jan 2025 13:19:54 -0500 Subject: [PATCH 10/56] minor fma cleanup (#57041) This removes the redundant `fma_llvm` function, and makes it so systems with Float16 fma can actually use it rather than the Float32 fallback path. --- base/floatfuncs.jl | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/base/floatfuncs.jl b/base/floatfuncs.jl index 2c26f7cff1133..f373325770ef9 100644 --- a/base/floatfuncs.jl +++ b/base/floatfuncs.jl @@ -276,6 +276,9 @@ significantly more expensive than `x*y+z`. `fma` is used to improve accuracy in algorithms. See [`muladd`](@ref). """ function fma end +function fma_emulated(a::Float16, b::Float16, c::Float16) + Float16(muladd(Float32(a), Float32(b), Float32(c))) #don't use fma if the hardware doesn't have it. +end function fma_emulated(a::Float32, b::Float32, c::Float32)::Float32 ab = Float64(a) * b res = ab+c @@ -348,19 +351,14 @@ function fma_emulated(a::Float64, b::Float64,c::Float64) s = (abs(abhi) > abs(c)) ? (abhi-r+c+ablo) : (c-r+abhi+ablo) return r+s end -fma_llvm(x::Float32, y::Float32, z::Float32) = fma_float(x, y, z) -fma_llvm(x::Float64, y::Float64, z::Float64) = fma_float(x, y, z) # Disable LLVM's fma if it is incorrect, e.g. because LLVM falls back # onto a broken system libm; if so, use a software emulated fma -@assume_effects :consistent fma(x::Float32, y::Float32, z::Float32) = Core.Intrinsics.have_fma(Float32) ? fma_llvm(x,y,z) : fma_emulated(x,y,z) -@assume_effects :consistent fma(x::Float64, y::Float64, z::Float64) = Core.Intrinsics.have_fma(Float64) ? fma_llvm(x,y,z) : fma_emulated(x,y,z) - -function fma(a::Float16, b::Float16, c::Float16) - Float16(muladd(Float32(a), Float32(b), Float32(c))) #don't use fma if the hardware doesn't have it. +@assume_effects :consistent function fma(x::T, y::T, z::T) where {T<:IEEEFloat} + Core.Intrinsics.have_fma(T) ? fma_float(x,y,z) : fma_emulated(x,y,z) end -# This is necessary at least on 32-bit Intel Linux, since fma_llvm may +# This is necessary at least on 32-bit Intel Linux, since fma_float may # have called glibc, and some broken glibc fma implementations don't # properly restore the rounding mode Rounding.setrounding_raw(Float32, Rounding.JL_FE_TONEAREST) From b70761f76dee4fc4194f61ac6a53d7e05c6f3c92 Mon Sep 17 00:00:00 2001 From: Neven Sajko <4944410+nsajko@users.noreply.github.com> Date: Tue, 21 Jan 2025 21:19:16 +0100 Subject: [PATCH 11/56] allow `rand!` with explicit SIMD to be used for various dense arrays (#57101) --- stdlib/Random/src/XoshiroSimd.jl | 7 ++++--- stdlib/Random/test/runtests.jl | 4 +++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/stdlib/Random/src/XoshiroSimd.jl b/stdlib/Random/src/XoshiroSimd.jl index 58544714dd9f5..f77be4a347111 100644 --- a/stdlib/Random/src/XoshiroSimd.jl +++ b/stdlib/Random/src/XoshiroSimd.jl @@ -292,20 +292,21 @@ end return i end +const MutableDenseArray = Union{Base.MutableDenseArrayType{T}, UnsafeView{T}} where {T} -function rand!(rng::Union{TaskLocalRNG, Xoshiro}, dst::Array{T}, ::SamplerTrivial{CloseOpen01{T}}) where {T<:Union{Float16,Float32,Float64}} +function rand!(rng::Union{TaskLocalRNG, Xoshiro}, dst::MutableDenseArray{T}, ::SamplerTrivial{CloseOpen01{T}}) where {T<:Union{Float16,Float32,Float64}} GC.@preserve dst xoshiro_bulk(rng, convert(Ptr{UInt8}, pointer(dst)), length(dst)*sizeof(T), T, xoshiroWidth(), _bits2float) dst end for T in BitInteger_types - @eval function rand!(rng::Union{TaskLocalRNG, Xoshiro}, dst::Union{Array{$T}, UnsafeView{$T}}, ::SamplerType{$T}) + @eval function rand!(rng::Union{TaskLocalRNG, Xoshiro}, dst::MutableDenseArray{$T}, ::SamplerType{$T}) GC.@preserve dst xoshiro_bulk(rng, convert(Ptr{UInt8}, pointer(dst)), length(dst)*sizeof($T), UInt8, xoshiroWidth()) dst end end -function rand!(rng::Union{TaskLocalRNG, Xoshiro}, dst::Array{Bool}, ::SamplerType{Bool}) +function rand!(rng::Union{TaskLocalRNG, Xoshiro}, dst::MutableDenseArray{Bool}, ::SamplerType{Bool}) GC.@preserve dst xoshiro_bulk(rng, convert(Ptr{UInt8}, pointer(dst)), length(dst), Bool, xoshiroWidth()) dst end diff --git a/stdlib/Random/test/runtests.jl b/stdlib/Random/test/runtests.jl index 9b46951f63ff5..13edf2e6553ec 100644 --- a/stdlib/Random/test/runtests.jl +++ b/stdlib/Random/test/runtests.jl @@ -370,9 +370,10 @@ for rng in ([], [MersenneTwister(0)], [RandomDevice()], [Xoshiro()]) a8 = rand!(rng..., GenericArray{T}(undef, 2, 3), cc) ::GenericArray{T, 2} a9 = rand!(rng..., OffsetArray(Array{T}(undef, 5), 9), cc) ::OffsetArray{T, 1} a10 = rand!(rng..., OffsetArray(Array{T}(undef, 2, 3), (-2, 4)), cc) ::OffsetArray{T, 2} + a11 = rand!(rng..., Memory{T}(undef, 5), cc) ::Memory{T} @test size(a1) == (5,) @test size(a2) == size(a3) == (2, 3) - for a in [a0, a1..., a2..., a3..., a4..., a5..., a6..., a7..., a8..., a9..., a10...] + for a in [a0, a1..., a2..., a3..., a4..., a5..., a6..., a7..., a8..., a9..., a10..., a11...] if C isa Type @test a isa C else @@ -392,6 +393,7 @@ for rng in ([], [MersenneTwister(0)], [RandomDevice()], [Xoshiro()]) (T <: Tuple || T <: Pair) && continue X = T == Bool ? T[0,1] : T[0,1,2] for A in (Vector{T}(undef, 5), + Memory{T}(undef, 5), Matrix{T}(undef, 2, 3), GenericArray{T}(undef, 5), GenericArray{T}(undef, 2, 3), From e03de0b883c044723d84cb4a4f93a6dd1ba99728 Mon Sep 17 00:00:00 2001 From: Max Horn Date: Tue, 21 Jan 2025 23:22:37 +0100 Subject: [PATCH 12/56] Remove `jl_task_stack_buffer` (again) (#57116) This was removed previously in PR #54527 but had to be reverted in PR #54559 as one usage remained (more by accident then by design). This has since then been resolved. This is also part of PR #56477 but that seems stalled right now, and in fact merging parts of it now may make the review of it easier later on... --- src/jl_exported_funcs.inc | 1 - src/julia_gcext.h | 9 --------- src/task.c | 28 ---------------------------- 3 files changed, 38 deletions(-) diff --git a/src/jl_exported_funcs.inc b/src/jl_exported_funcs.inc index b92380df7a49c..c1b29a091511b 100644 --- a/src/jl_exported_funcs.inc +++ b/src/jl_exported_funcs.inc @@ -449,7 +449,6 @@ XX(jl_tagged_gensym) \ XX(jl_take_buffer) \ XX(jl_task_get_next) \ - XX(jl_task_stack_buffer) \ XX(jl_termios_size) \ XX(jl_test_cpu_feature) \ XX(jl_threadid) \ diff --git a/src/julia_gcext.h b/src/julia_gcext.h index 05140e4b09ace..e124f58a09402 100644 --- a/src/julia_gcext.h +++ b/src/julia_gcext.h @@ -135,15 +135,6 @@ JL_DLLEXPORT int jl_gc_conservative_gc_support_enabled(void); // NOTE: Only valid to call from within a GC context. JL_DLLEXPORT jl_value_t *jl_gc_internal_obj_base_ptr(void *p) JL_NOTSAFEPOINT; -// Return a non-null pointer to the start of the stack area if the task -// has an associated stack buffer. In that case, *size will also contain -// the size of that stack buffer upon return. Also, if task is a thread's -// current task, that thread's id will be stored in *tid; otherwise, -// *tid will be set to -1. -// -// DEPRECATED: use jl_active_task_stack() instead. -JL_DLLEXPORT void *jl_task_stack_buffer(jl_task_t *task, size_t *size, int *tid); - // Query the active and total stack range for the given task, and set // *active_start and *active_end respectively *total_start and *total_end // accordingly. The range for the active part is a best-effort approximation diff --git a/src/task.c b/src/task.c index d56d60eb58cb5..37e7f0e1f5440 100644 --- a/src/task.c +++ b/src/task.c @@ -352,34 +352,6 @@ void JL_NORETURN jl_finish_task(jl_task_t *ct) abort(); } -JL_DLLEXPORT void *jl_task_stack_buffer(jl_task_t *task, size_t *size, int *ptid) -{ - size_t off = 0; -#ifndef _OS_WINDOWS_ - jl_ptls_t ptls0 = jl_atomic_load_relaxed(&jl_all_tls_states)[0]; - if (ptls0->root_task == task) { - // See jl_init_root_task(). The root task of the main thread - // has its buffer enlarged by an artificial 3000000 bytes, but - // that means that the start of the buffer usually points to - // inaccessible memory. We need to correct for this. - off = ROOT_TASK_STACK_ADJUSTMENT; - } -#endif - jl_ptls_t ptls2 = task->ptls; - *ptid = -1; - if (ptls2) { - *ptid = jl_atomic_load_relaxed(&task->tid); -#ifdef COPY_STACKS - if (task->ctx.copy_stack) { - *size = ptls2->stacksize; - return (char *)ptls2->stackbase - *size; - } -#endif - } - *size = task->ctx.bufsz - off; - return (void *)((char *)task->ctx.stkbuf + off); -} - JL_DLLEXPORT void jl_active_task_stack(jl_task_t *task, char **active_start, char **active_end, char **total_start, char **total_end) From f91436eae7265a01bff17e35887cd9b8e15c8fdc Mon Sep 17 00:00:00 2001 From: Cody Tapscott <84105208+topolarity@users.noreply.github.com> Date: Tue, 21 Jan 2025 18:14:11 -0500 Subject: [PATCH 13/56] Add test for #57115 (#57120) Closes #57115. This issue was accidentally resolved on master by #54894, but it is important enough that we should have test coverage for it. --- test/precompile.jl | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/test/precompile.jl b/test/precompile.jl index 78a96250600a4..55e97f5608b08 100644 --- a/test/precompile.jl +++ b/test/precompile.jl @@ -1094,6 +1094,17 @@ precompile_test_harness("invoke") do dir f44320(::Any) = 2 g44320() = invoke(f44320, Tuple{Any}, 0) g44320() + # Issue #57115 + f57115(@nospecialize(::Any)) = error("unimplemented") + function g57115(@nospecialize(x)) + if @noinline rand(Bool) + # Add an 'invoke' edge from 'foo' to 'bar' + Core.invoke(f57115, Tuple{Any}, x) + else + # ... and also an identical 'call' edge + @noinline f57115(x) + end + end # Adding new specializations should not invalidate `invoke`s function getlast(itr) @@ -1110,6 +1121,8 @@ precompile_test_harness("invoke") do dir """ module $CallerModule using $InvokeModule + import $InvokeModule: f57115, g57115 + # involving external modules callf(x) = f(x) callg(x) = x < 5 ? g(x) : invoke(g, Tuple{Real}, x) @@ -1130,6 +1143,8 @@ precompile_test_harness("invoke") do dir # Issue #44320 f44320(::Real) = 3 + # Issue #57115 + f57115(::Int) = 1 call_getlast(x) = getlast(x) @@ -1150,6 +1165,7 @@ precompile_test_harness("invoke") do dir @noinline internalnc(3) @noinline call_getlast([1,2,3]) end + precompile(g57115, (Any,)) # Now that we've precompiled, invalidate with a new method that overrides the `invoke` dispatch $InvokeModule.h(x::Integer) = -1 @@ -1217,6 +1233,30 @@ precompile_test_harness("invoke") do dir m = only(methods(M.g44320)) @test (m.specializations::Core.MethodInstance).cache.max_world == typemax(UInt) + m = only(methods(M.g57115)) + mi = m.specializations::Core.MethodInstance + + f_m = get_method_for_type(M.f57115, Any) + f_mi = f_m.specializations::Core.MethodInstance + + # Make sure that f57115(::Any) has a 'call' backedge to 'g57115' + has_f_call_backedge = false + i = 1 + while i ≤ length(f_mi.backedges) + if f_mi.backedges[i] isa DataType + # invoke edge - skip + i += 2 + else + caller = f_mi.backedges[i]::Core.CodeInstance + if caller.def === mi + has_f_call_backedge = true + break + end + i += 1 + end + end + @test has_f_call_backedge + m = which(MI.getlast, (Any,)) @test (m.specializations::Core.MethodInstance).cache.max_world == typemax(UInt) From 294c4a63a0cd37dff72a51b2d9210a36ae2ff174 Mon Sep 17 00:00:00 2001 From: Erik Schnetter Date: Wed, 22 Jan 2025 07:06:42 -0500 Subject: [PATCH 14/56] runtime_intrinsics.c: Remove duplicated line of code (#57123) --- src/runtime_intrinsics.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/runtime_intrinsics.c b/src/runtime_intrinsics.c index 1790b9bd8d106..90afa3fb6bddf 100644 --- a/src/runtime_intrinsics.c +++ b/src/runtime_intrinsics.c @@ -812,7 +812,6 @@ static void jl_##name##16(unsigned runtime_nbits, void *pa, void *pb, void *pr) runtime_nbits = 16; \ float R = OP(A, B); \ *(uint16_t*)pr = float_to_half(R); \ - *(uint16_t*)pr = float_to_half(R); \ } #define bi_intrinsic_bfloat(OP, name) \ @@ -903,7 +902,6 @@ static void jl_##name##16(unsigned runtime_nbits, void *pa, void *pb, void *pc, runtime_nbits = 16; \ float R = OP(A, B, C); \ *(uint16_t*)pr = float_to_half(R); \ - *(uint16_t*)pr = float_to_half(R); \ } #define ter_intrinsic_bfloat(OP, name) \ From 7f99e95377c61515a63aed6ca6500e837f34f69f Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Wed, 22 Jan 2025 13:40:53 +0100 Subject: [PATCH 15/56] bpart: Start enforcing minimum world age for const bparts (#57102) Currently, even though the binding partition system is implemented, it is largely enabled. New `const` definitions get magically "backdated" to the first world age in which the binding was undefined. Additionally, they do not get their own world age and there is currently no `latestworld` marker after `const` definitions. This PR changes this situation to give const markers their own world age with appropriate `latestworld` increments. Both of these are mandatory for `const` replacement to work. The other thing this PR does is prepare to remove the automatic "backdating". To see the difference, consider: ``` function foo($i) Core.eval(:(const x = $i)) x end ``` Without an intervening world age increment, this will throw an UndefVarError on this PR. I believe this is the best option for two reasons: 1. It will allow us infer these to `Union{}` in the future (thus letting inference prune dead code faster). 2. I think it is less confusing in terms of the world age semantics for `const` definitions to become active only after they are defined. To illustrate the second point, suppose we did keep the automatic backdating. Then we would have: ``` foo(1) => 1 foo(2) => 1 foo(3) => 2 ``` as opposed to on this PR: ``` foo(1) => UndefVarError foo(2) => 1 foo(3) => 2 ``` The semantics are consistent, of course, but I am concerned that an automatic backdating will give users the wrong mental model about world age, since it "fixes itself" the first time, but if you Revise it, it will give an unexpected answer. I think this would encourage accidentally bad code patterns that only break under Revise (where they are hard to debug). The counterpoint of course is that that not backdating is a more breaking choice. As with the rest of the 1.12-era world age semantics changes, I think taking a look at PkgEval will be helpful. --- Compiler/src/abstractinterpretation.jl | 11 ++-- Compiler/src/optimize.jl | 2 +- Compiler/src/ssair/ir.jl | 4 +- Compiler/src/ssair/legacy.jl | 2 +- Compiler/src/ssair/verify.jl | 12 +++- Compiler/src/tfuncs.jl | 2 +- Compiler/test/inline.jl | 4 +- base/Base.jl | 1 + base/client.jl | 12 ++-- base/essentials.jl | 6 ++ base/loading.jl | 22 +++++--- base/logging/logging.jl | 4 +- base/reflection.jl | 77 +++++++++++++++++++------- base/runtime_internals.jl | 16 ++++-- src/codegen.cpp | 34 ++++++++---- src/gf.c | 21 ++++--- src/init.c | 13 +++-- src/jlapi.c | 11 ++-- src/julia-syntax.scm | 14 ++++- src/julia.h | 1 - src/julia_internal.h | 14 +++-- src/method.c | 30 +++++++--- src/module.c | 43 +++++++------- src/staticdata.c | 15 ++++- src/toplevel.c | 38 +++++++++++-- stdlib/REPL/src/REPLCompletions.jl | 16 ++++-- stdlib/REPL/src/precompile.jl | 1 + stdlib/REPL/test/precompilation.jl | 2 +- stdlib/REPL/test/repl.jl | 8 +-- stdlib/Serialization/test/runtests.jl | 16 +++--- test/core.jl | 2 +- test/loading.jl | 1 + test/precompile.jl | 16 ++++-- test/runtests.jl | 6 +- test/syntax.jl | 1 + test/worlds.jl | 4 +- 36 files changed, 318 insertions(+), 164 deletions(-) diff --git a/Compiler/src/abstractinterpretation.jl b/Compiler/src/abstractinterpretation.jl index 300feeae53e9a..2b1a7fb2dd448 100644 --- a/Compiler/src/abstractinterpretation.jl +++ b/Compiler/src/abstractinterpretation.jl @@ -3456,10 +3456,10 @@ world_range(ir::IRCode) = ir.valid_worlds world_range(ci::CodeInfo) = WorldRange(ci.min_world, ci.max_world) world_range(compact::IncrementalCompact) = world_range(compact.ir) -function force_binding_resolution!(g::GlobalRef) +function force_binding_resolution!(g::GlobalRef, world::UInt) # Force resolution of the binding # TODO: This will go away once we switch over to fully partitioned semantics - ccall(:jl_globalref_boundp, Cint, (Any,), g) + ccall(:jl_force_binding_resolution, Cvoid, (Any, Csize_t), g, world) return nothing end @@ -3477,7 +3477,7 @@ function abstract_eval_globalref_type(g::GlobalRef, src::Union{CodeInfo, IRCode, # This method is surprisingly hot. For performance, don't ask the runtime to resolve # the binding unless necessary - doing so triggers an additional lookup, which though # not super expensive is hot enough to show up in benchmarks. - force_binding_resolution!(g) + force_binding_resolution!(g, min_world(worlds)) return abstract_eval_globalref_type(g, src, false) end # return Union{} @@ -3490,7 +3490,7 @@ function abstract_eval_globalref_type(g::GlobalRef, src::Union{CodeInfo, IRCode, end function lookup_binding_partition!(interp::AbstractInterpreter, g::GlobalRef, sv::AbsIntState) - force_binding_resolution!(g) + force_binding_resolution!(g, get_inference_world(interp)) partition = lookup_binding_partition(get_inference_world(interp), g) update_valid_age!(sv, WorldRange(partition.min_world, partition.max_world)) partition @@ -3539,7 +3539,8 @@ function abstract_eval_globalref(interp::AbstractInterpreter, g::GlobalRef, saw_ partition = abstract_eval_binding_partition!(interp, g, sv) ret = abstract_eval_partition_load(interp, partition) if ret.rt !== Union{} && ret.exct === UndefVarError && InferenceParams(interp).assume_bindings_static - if isdefined(g, :binding) && isdefined(g.binding, :value) + b = convert(Core.Binding, g) + if isdefined(b, :value) ret = RTEffects(ret.rt, Union{}, Effects(generic_getglobal_effects, nothrow=true)) end # We do not assume in general that assigned global bindings remain assigned. diff --git a/Compiler/src/optimize.jl b/Compiler/src/optimize.jl index 12b2f3c9a269f..9f74f028507cd 100644 --- a/Compiler/src/optimize.jl +++ b/Compiler/src/optimize.jl @@ -1286,7 +1286,7 @@ function convert_to_ircode(ci::CodeInfo, sv::OptimizationState) # types of call arguments only once `slot2reg` converts this `IRCode` to the SSA form # and eliminates slots (see below) argtypes = sv.slottypes - return IRCode(stmts, sv.cfg, di, argtypes, meta, sv.sptypes, WorldRange(ci.min_world, ci.max_world)) + return IRCode(stmts, sv.cfg, di, argtypes, meta, sv.sptypes, world_range(ci)) end function process_meta!(meta::Vector{Expr}, @nospecialize stmt) diff --git a/Compiler/src/ssair/ir.jl b/Compiler/src/ssair/ir.jl index 9103dba04fa54..f86ada2309ddc 100644 --- a/Compiler/src/ssair/ir.jl +++ b/Compiler/src/ssair/ir.jl @@ -434,7 +434,7 @@ struct IRCode function IRCode(stmts::InstructionStream, cfg::CFG, debuginfo::DebugInfoStream, argtypes::Vector{Any}, meta::Vector{Expr}, sptypes::Vector{VarState}, valid_worlds=WorldRange(typemin(UInt), typemax(UInt))) - return new(stmts, argtypes, sptypes, debuginfo, cfg, NewNodeStream(), meta) + return new(stmts, argtypes, sptypes, debuginfo, cfg, NewNodeStream(), meta, valid_worlds) end function IRCode(ir::IRCode, stmts::InstructionStream, cfg::CFG, new_nodes::NewNodeStream) di = ir.debuginfo @@ -1462,7 +1462,7 @@ function process_node!(compact::IncrementalCompact, result_idx::Int, inst::Instr result[result_idx][:stmt] = GotoNode(label) result_idx += 1 elseif isa(stmt, GlobalRef) - total_flags = IR_FLAG_CONSISTENT | IR_FLAG_EFFECT_FREE + total_flags = IR_FLAG_CONSISTENT | IR_FLAG_EFFECT_FREE | IR_FLAG_NOTHROW flag = result[result_idx][:flag] if has_flag(flag, total_flags) ssa_rename[idx] = stmt diff --git a/Compiler/src/ssair/legacy.jl b/Compiler/src/ssair/legacy.jl index 675ca2dea9b32..64a39c72ad9eb 100644 --- a/Compiler/src/ssair/legacy.jl +++ b/Compiler/src/ssair/legacy.jl @@ -44,7 +44,7 @@ function inflate_ir!(ci::CodeInfo, sptypes::Vector{VarState}, argtypes::Vector{A di = DebugInfoStream(nothing, ci.debuginfo, nstmts) stmts = InstructionStream(code, ssavaluetypes, info, di.codelocs, ci.ssaflags) meta = Expr[] - return IRCode(stmts, cfg, di, argtypes, meta, sptypes, WorldRange(ci.min_world, ci.max_world)) + return IRCode(stmts, cfg, di, argtypes, meta, sptypes, world_range(ci)) end """ diff --git a/Compiler/src/ssair/verify.jl b/Compiler/src/ssair/verify.jl index 12eb09be693f3..fa16bdcc7ab19 100644 --- a/Compiler/src/ssair/verify.jl +++ b/Compiler/src/ssair/verify.jl @@ -12,7 +12,7 @@ end if !isdefined(@__MODULE__, Symbol("@verify_error")) macro verify_error(arg) - arg isa String && return esc(:(print && println(stderr, $arg))) + arg isa String && return esc(:(print && println($(GlobalRef(Core, :stderr)), $arg))) isexpr(arg, :string) || error("verify_error macro expected a string expression") pushfirst!(arg.args, GlobalRef(Core, :stderr)) pushfirst!(arg.args, :println) @@ -61,8 +61,14 @@ function check_op(ir::IRCode, domtree::DomTree, @nospecialize(op), use_bb::Int, raise_error() end elseif isa(op, GlobalRef) - if !isdefined(op.mod, op.name) || !isconst(op.mod, op.name) - @verify_error "Unbound GlobalRef not allowed in value position" + force_binding_resolution!(op, min_world(ir.valid_worlds)) + bpart = lookup_binding_partition(min_world(ir.valid_worlds), op) + while is_some_imported(binding_kind(bpart)) && max_world(ir.valid_worlds) <= bpart.max_world + imported_binding = partition_restriction(bpart)::Core.Binding + bpart = lookup_binding_partition(min_world(ir.valid_worlds), imported_binding) + end + if !is_defined_const_binding(binding_kind(bpart)) || (bpart.max_world < max_world(ir.valid_worlds)) + @verify_error "Unbound or partitioned GlobalRef not allowed in value position" raise_error() end elseif isa(op, Expr) diff --git a/Compiler/src/tfuncs.jl b/Compiler/src/tfuncs.jl index fe21f6381e8cd..74c8026ca0cf5 100644 --- a/Compiler/src/tfuncs.jl +++ b/Compiler/src/tfuncs.jl @@ -1109,7 +1109,7 @@ function _getfield_tfunc_const(@nospecialize(sv), name::Const) if isa(sv, DataType) && nv == DATATYPE_TYPES_FIELDINDEX && isdefined(sv, nv) return Const(getfield(sv, nv)) end - if isconst(typeof(sv), nv) + if !isa(sv, Module) && isconst(typeof(sv), nv) if isdefined(sv, nv) return Const(getfield(sv, nv)) end diff --git a/Compiler/test/inline.jl b/Compiler/test/inline.jl index 5f95fb761859e..b8ff14405391d 100644 --- a/Compiler/test/inline.jl +++ b/Compiler/test/inline.jl @@ -149,7 +149,7 @@ end (src, _) = only(code_typed(sum27403, Tuple{Vector{Int}})) @test !any(src.code) do x - x isa Expr && x.head === :invoke && x.args[2] !== Core.GlobalRef(Base, :throw_boundserror) + x isa Expr && x.head === :invoke && !(x.args[2] in (Core.GlobalRef(Base, :throw_boundserror), Base.throw_boundserror)) end end @@ -313,7 +313,7 @@ end const _a_global_array = [1] f_inline_global_getindex() = _a_global_array[1] let ci = code_typed(f_inline_global_getindex, Tuple{})[1].first - @test any(x->(isexpr(x, :call) && x.args[1] === GlobalRef(Base, :memoryrefget)), ci.code) + @test any(x->(isexpr(x, :call) && x.args[1] in (GlobalRef(Base, :memoryrefget), Base.memoryrefget)), ci.code) end # Issue #29114 & #36087 - Inlining of non-tuple splats diff --git a/base/Base.jl b/base/Base.jl index 20b1636c29a8d..04f732a4309c9 100644 --- a/base/Base.jl +++ b/base/Base.jl @@ -401,6 +401,7 @@ end # we know whether the .ji can just give the Base copy or not. # TODO: We may want to do this earlier to avoid TOCTOU issues. const _compiler_require_dependencies = Any[] +@Core.latestworld for i = 1:length(_included_files) isassigned(_included_files, i) || continue (mod, file) = _included_files[i] diff --git a/base/client.jl b/base/client.jl index e95d518d3e501..2527d382c695d 100644 --- a/base/client.jl +++ b/base/client.jl @@ -299,7 +299,7 @@ function exec_options(opts) elseif cmd == 'm' entrypoint = push!(split(arg, "."), "main") Base.eval(Main, Expr(:import, Expr(:., Symbol.(entrypoint)...))) - if !should_use_main_entrypoint() + if !invokelatest(should_use_main_entrypoint) error("`main` in `$arg` not declared as entry point (use `@main` to do so)") end return false @@ -408,8 +408,7 @@ function load_InteractiveUtils(mod::Module=Main) return nothing end end - Core.eval(mod, :(using Base.MainInclude.InteractiveUtils)) - return MainInclude.InteractiveUtils + return Core.eval(mod, :(using Base.MainInclude.InteractiveUtils; Base.MainInclude.InteractiveUtils)) end function load_REPL() @@ -556,11 +555,12 @@ function _start() local ret = 0 try repl_was_requested = exec_options(JLOptions()) - if should_use_main_entrypoint() && !is_interactive + if invokelatest(should_use_main_entrypoint) && !is_interactive + main = invokelatest(getglobal, Main, :main) if Base.generating_output() - precompile(Main.main, (typeof(ARGS),)) + precompile(main, (typeof(ARGS),)) else - ret = invokelatest(Main.main, ARGS) + ret = invokelatest(main, ARGS) end elseif (repl_was_requested || is_interactive) # Run the Base `main`, which will either load the REPL stdlib diff --git a/base/essentials.jl b/base/essentials.jl index fa5cf79192f56..58e4ce1125093 100644 --- a/base/essentials.jl +++ b/base/essentials.jl @@ -1266,6 +1266,9 @@ arbitrary code in fixed worlds. `world` may be `UnitRange`, in which case the ma will error unless the binding is valid and has the same value across the entire world range. +As a special case, the world `∞` always refers to the latest world, even if that world +is newer than the world currently running. + The `@world` macro is primarily used in the printing of bindings that are no longer available in the current world. @@ -1290,6 +1293,9 @@ julia> fold This functionality requires at least Julia 1.12. """ macro world(sym, world) + if world == :∞ + world = Expr(:call, get_world_counter) + end if isa(sym, Symbol) return :($(_resolve_in_world)($(esc(world)), $(QuoteNode(GlobalRef(__module__, sym))))) elseif isa(sym, GlobalRef) diff --git a/base/loading.jl b/base/loading.jl index 4193aae13b96a..240406292246b 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -2364,6 +2364,18 @@ function require(into::Module, mod::Symbol) return invoke_in_world(world, __require, into, mod) end +function check_for_hint(into, mod) + return begin + if isdefined(into, mod) && getfield(into, mod) isa Module + true, "." + elseif isdefined(parentmodule(into), mod) && getfield(parentmodule(into), mod) isa Module + true, ".." + else + false, "" + end + end +end + function __require(into::Module, mod::Symbol) if into === __toplevel__ && generating_output(#=incremental=#true) error("`using/import $mod` outside of a Module detected. Importing a package outside of a module \ @@ -2377,15 +2389,7 @@ function __require(into::Module, mod::Symbol) if uuidkey_env === nothing where = PkgId(into) if where.uuid === nothing - hint, dots = begin - if isdefined(into, mod) && getfield(into, mod) isa Module - true, "." - elseif isdefined(parentmodule(into), mod) && getfield(parentmodule(into), mod) isa Module - true, ".." - else - false, "" - end - end + hint, dots = invokelatest(check_for_hint, into, mod) hint_message = hint ? ", maybe you meant `import/using $(dots)$(mod)`" : "" install_message = if mod != :Pkg start_sentence = hint ? "Otherwise, run" : "Run" diff --git a/base/logging/logging.jl b/base/logging/logging.jl index 41721e84cc934..a1a8417bcb388 100644 --- a/base/logging/logging.jl +++ b/base/logging/logging.jl @@ -372,7 +372,7 @@ function logmsg_code(_module, file, line, level, message, exs...) kwargs = (;$(log_data.kwargs...)) true else - @invokelatest logging_error(logger, level, _module, group, id, file, line, err, false) + @invokelatest $(logging_error)(logger, level, _module, group, id, file, line, err, false) false end end @@ -384,7 +384,7 @@ function logmsg_code(_module, file, line, level, message, exs...) kwargs = (;$(log_data.kwargs...)) true catch err - @invokelatest logging_error(logger, level, _module, group, id, file, line, err, true) + @invokelatest $(logging_error)(logger, level, _module, group, id, file, line, err, true) false end end diff --git a/base/reflection.jl b/base/reflection.jl index f9c5dd9765533..78e701692a2a7 100644 --- a/base/reflection.jl +++ b/base/reflection.jl @@ -1362,6 +1362,18 @@ macro invoke(ex) return esc(out) end +apply_gr(gr::GlobalRef, @nospecialize args...) = getglobal(gr.mod, gr.name)(args...) +apply_gr_kw(@nospecialize(kwargs::NamedTuple), gr::GlobalRef, @nospecialize args...) = Core.kwcall(kwargs, getglobal(gr.mod, gr.name), args...) + +function invokelatest_gr(gr::GlobalRef, @nospecialize args...; kwargs...) + @inline + kwargs = merge(NamedTuple(), kwargs) + if isempty(kwargs) + return Core._call_latest(apply_gr, gr, args...) + end + return Core._call_latest(apply_gr_kw, kwargs, gr, args...) +end + """ @invokelatest f(args...; kwargs...) @@ -1375,22 +1387,11 @@ It also supports the following syntax: - `@invokelatest xs[i]` expands to `Base.invokelatest(getindex, xs, i)` - `@invokelatest xs[i] = v` expands to `Base.invokelatest(setindex!, xs, v, i)` -```jldoctest -julia> @macroexpand @invokelatest f(x; kw=kwv) -:(Base.invokelatest(f, x; kw = kwv)) - -julia> @macroexpand @invokelatest x.f -:(Base.invokelatest(Base.getproperty, x, :f)) - -julia> @macroexpand @invokelatest x.f = v -:(Base.invokelatest(Base.setproperty!, x, :f, v)) - -julia> @macroexpand @invokelatest xs[i] -:(Base.invokelatest(Base.getindex, xs, i)) - -julia> @macroexpand @invokelatest xs[i] = v -:(Base.invokelatest(Base.setindex!, xs, v, i)) -``` +!!! note + If `f` is a global, it will be resolved consistently + in the (latest) world as the call target. However, all other arguments + (as well as `f` itself if it is not a literal global) will be evaluated + in the current world age. !!! compat "Julia 1.7" This macro requires Julia 1.7 or later. @@ -1404,11 +1405,45 @@ julia> @macroexpand @invokelatest xs[i] = v macro invokelatest(ex) topmod = _topmod(__module__) f, args, kwargs = destructure_callex(topmod, ex) - out = Expr(:call, GlobalRef(Base, :invokelatest)) - isempty(kwargs) || push!(out.args, Expr(:parameters, kwargs...)) - push!(out.args, f) - append!(out.args, args) - return esc(out) + + if !isa(f, GlobalRef) + out_f = Expr(:call, GlobalRef(Base, :invokelatest)) + isempty(kwargs) || push!(out_f.args, Expr(:parameters, kwargs...)) + + if isexpr(f, :(.)) + s = gensym() + check = quote + $s = $(f.args[1]) + isa($s, Module) + end + push!(out_f.args, Expr(:(.), s, f.args[2])) + else + push!(out_f.args, f) + end + append!(out_f.args, args) + + if @isdefined(s) + f = :(GlobalRef($s, $(f.args[2]))) + elseif !isa(f, Symbol) + return esc(out_f) + else + check = :($(Expr(:isglobal, f))) + end + end + + out_gr = Expr(:call, GlobalRef(Base, :invokelatest_gr)) + isempty(kwargs) || push!(out_gr.args, Expr(:parameters, kwargs...)) + push!(out_gr.args, isa(f, GlobalRef) ? QuoteNode(f) : + isa(f, Symbol) ? QuoteNode(GlobalRef(__module__, f)) : + f) + append!(out_gr.args, args) + + if isa(f, GlobalRef) + return esc(out_gr) + end + + # f::Symbol + return esc(:($check ? $out_gr : $out_f)) end function destructure_callex(topmod::Module, @nospecialize(ex)) diff --git a/base/runtime_internals.jl b/base/runtime_internals.jl index 67694e533ac47..964e8063dd5af 100644 --- a/base/runtime_internals.jl +++ b/base/runtime_internals.jl @@ -239,12 +239,16 @@ function lookup_binding_partition(world::UInt, b::Core.Binding) ccall(:jl_get_binding_partition, Ref{Core.BindingPartition}, (Any, UInt), b, world) end -function lookup_binding_partition(world::UInt, gr::Core.GlobalRef) +function convert(::Type{Core.Binding}, gr::Core.GlobalRef) if isdefined(gr, :binding) - b = gr.binding + return gr.binding else - b = ccall(:jl_get_module_binding, Ref{Core.Binding}, (Any, Any, Cint), gr.mod, gr.name, true) + return ccall(:jl_get_module_binding, Ref{Core.Binding}, (Any, Any, Cint), gr.mod, gr.name, true) end +end + +function lookup_binding_partition(world::UInt, gr::Core.GlobalRef) + b = convert(Core.Binding, gr) return lookup_binding_partition(world, b) end @@ -420,7 +424,11 @@ end """ isconst(t::DataType, s::Union{Int,Symbol}) -> Bool -Determine whether a field `s` is declared `const` in a given type `t`. +Determine whether a field `s` is const in a given type `t` +in the sense that a read from said field is consistent +for egal objects. Note in particular that out-of-bounds +fields are considered const under this definition (because +they always throw). """ function isconst(@nospecialize(t::Type), s::Symbol) @_foldable_meta diff --git a/src/codegen.cpp b/src/codegen.cpp index 7cfcc52db29c7..e047632923f68 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -3136,8 +3136,11 @@ static jl_value_t *static_eval(jl_codectx_t &ctx, jl_value_t *ex) { if (jl_is_symbol(ex)) { jl_sym_t *sym = (jl_sym_t*)ex; - if (jl_is_const(ctx.module, sym)) - return jl_get_global(ctx.module, sym); + jl_binding_t *bnd = jl_get_module_binding(ctx.module, sym, 0); + jl_binding_partition_t *bpart = jl_get_binding_partition_all(bnd, ctx.min_world, ctx.max_world); + jl_ptr_kind_union_t pku = jl_walk_binding_inplace_all(&bnd, &bpart, ctx.min_world, ctx.max_world); + if (jl_bkind_is_some_constant(decode_restriction_kind(pku))) + return decode_restriction_value(pku); return NULL; } if (jl_is_slotnumber(ex) || jl_is_argument(ex)) @@ -3158,11 +3161,15 @@ static jl_value_t *static_eval(jl_codectx_t &ctx, jl_value_t *ex) jl_sym_t *s = NULL; if (jl_is_globalref(ex)) { s = jl_globalref_name(ex); - jl_binding_t *b = jl_get_binding(jl_globalref_mod(ex), s); - jl_value_t *v = jl_get_binding_value_if_const(b); + jl_binding_t *bnd = jl_get_module_binding(jl_globalref_mod(ex), s, 0); + jl_binding_partition_t *bpart = jl_get_binding_partition_all(bnd, ctx.min_world, ctx.max_world); + jl_ptr_kind_union_t pku = jl_walk_binding_inplace_all(&bnd, &bpart, ctx.min_world, ctx.max_world); + jl_value_t *v = NULL; + if (jl_bkind_is_some_constant(decode_restriction_kind(pku))) + v = decode_restriction_value(pku); if (v) { - if (b->deprecated) - cg_bdw(ctx, s, b); + if (bnd->deprecated) + cg_bdw(ctx, s, bnd); return v; } return NULL; @@ -3181,11 +3188,15 @@ static jl_value_t *static_eval(jl_codectx_t &ctx, jl_value_t *ex) // Assumes that the module is rooted somewhere. s = (jl_sym_t*)static_eval(ctx, jl_exprarg(e, 2)); if (s && jl_is_symbol(s)) { - jl_binding_t *b = jl_get_binding(m, s); - jl_value_t *v = jl_get_binding_value_if_const(b); + jl_binding_t *bnd = jl_get_module_binding(m, s, 0); + jl_binding_partition_t *bpart = jl_get_binding_partition_all(bnd, ctx.min_world, ctx.max_world); + jl_ptr_kind_union_t pku = jl_walk_binding_inplace_all(&bnd, &bpart, ctx.min_world, ctx.max_world); + jl_value_t *v = NULL; + if (jl_bkind_is_some_constant(decode_restriction_kind(pku))) + v = decode_restriction_value(pku); if (v) { - if (b->deprecated) - cg_bdw(ctx, s, b); + if (bnd->deprecated) + cg_bdw(ctx, s, bnd); return v; } } @@ -3465,14 +3476,13 @@ static jl_cgval_t emit_globalref(jl_codectx_t &ctx, jl_module_t *mod, jl_sym_t * return mark_julia_const(ctx, constval); } } - if (!bpart) { + if (!bpart || decode_restriction_kind(pku) != BINDING_KIND_GLOBAL) { return emit_globalref_runtime(ctx, bnd, mod, name); } Value *bp = julia_binding_gv(ctx, bnd); if (bnd->deprecated) { cg_bdw(ctx, name, bnd); } - assert(decode_restriction_kind(pku) == BINDING_KIND_GLOBAL); jl_value_t *ty = decode_restriction_value(pku); bp = julia_binding_pvalue(ctx, bp); if (ty == nullptr) diff --git a/src/gf.c b/src/gf.c index ea25ed2eae4ff..710dda208f0b2 100644 --- a/src/gf.c +++ b/src/gf.c @@ -292,7 +292,8 @@ jl_datatype_t *jl_mk_builtin_func(jl_datatype_t *dt, const char *name, jl_fptr_a { jl_sym_t *sname = jl_symbol(name); if (dt == NULL) { - jl_value_t *f = jl_new_generic_function_with_supertype(sname, jl_core_module, jl_builtin_type); + // Builtins are specially considered available from world 0 + jl_value_t *f = jl_new_generic_function_with_supertype(sname, jl_core_module, jl_builtin_type, 0); jl_set_const(jl_core_module, sname, f); dt = (jl_datatype_t*)jl_typeof(f); } @@ -3790,10 +3791,8 @@ jl_value_t *jl_gf_invoke_by_method(jl_method_t *method, jl_value_t *gf, jl_value return _jl_invoke(gf, args, nargs - 1, mfunc, world); } -// Return value is rooted globally -jl_function_t *jl_new_generic_function_with_supertype(jl_sym_t *name, jl_module_t *module, jl_datatype_t *st) +jl_sym_t *jl_gf_supertype_name(jl_sym_t *name) { - // type name is function name prefixed with # size_t l = strlen(jl_symbol_name(name)); char *prefixed; prefixed = (char*)malloc_s(l+2); @@ -3801,6 +3800,14 @@ jl_function_t *jl_new_generic_function_with_supertype(jl_sym_t *name, jl_module_ strcpy(&prefixed[1], jl_symbol_name(name)); jl_sym_t *tname = jl_symbol(prefixed); free(prefixed); + return tname; +} + +// Return value is rooted globally +jl_function_t *jl_new_generic_function_with_supertype(jl_sym_t *name, jl_module_t *module, jl_datatype_t *st, size_t new_world) +{ + // type name is function name prefixed with # + jl_sym_t *tname = jl_gf_supertype_name(name); jl_datatype_t *ftype = (jl_datatype_t*)jl_new_datatype( tname, module, st, jl_emptysvec, jl_emptysvec, jl_emptysvec, jl_emptysvec, 0, 0, 0); @@ -3808,7 +3815,7 @@ jl_function_t *jl_new_generic_function_with_supertype(jl_sym_t *name, jl_module_ JL_GC_PUSH1(&ftype); ftype->name->mt->name = name; jl_gc_wb(ftype->name->mt, name); - jl_set_const(module, tname, (jl_value_t*)ftype); + jl_declare_constant_val3(NULL, module, tname, (jl_value_t*)ftype, BINDING_KIND_CONST, new_world); jl_value_t *f = jl_new_struct(ftype); ftype->instance = f; jl_gc_wb(ftype, f); @@ -3816,9 +3823,9 @@ jl_function_t *jl_new_generic_function_with_supertype(jl_sym_t *name, jl_module_ return (jl_function_t*)f; } -jl_function_t *jl_new_generic_function(jl_sym_t *name, jl_module_t *module) +jl_function_t *jl_new_generic_function(jl_sym_t *name, jl_module_t *module, size_t new_world) { - return jl_new_generic_function_with_supertype(name, module, jl_function_type); + return jl_new_generic_function_with_supertype(name, module, jl_function_type, new_world); } struct ml_matches_env { diff --git a/src/init.c b/src/init.c index 7b41e63e98455..e69467c75bd73 100644 --- a/src/init.c +++ b/src/init.c @@ -249,6 +249,8 @@ JL_DLLEXPORT void jl_atexit_hook(int exitcode) JL_NOTSAFEPOINT_ENTER } if (jl_base_module) { + size_t last_age = ct->world_age; + ct->world_age = jl_get_world_counter(); jl_value_t *f = jl_get_global(jl_base_module, jl_symbol("_atexit")); if (f != NULL) { jl_value_t **fargs; @@ -257,10 +259,7 @@ JL_DLLEXPORT void jl_atexit_hook(int exitcode) JL_NOTSAFEPOINT_ENTER fargs[1] = jl_box_int32(exitcode); JL_TRY { assert(ct); - size_t last_age = ct->world_age; - ct->world_age = jl_get_world_counter(); jl_apply(fargs, 2); - ct->world_age = last_age; } JL_CATCH { jl_printf((JL_STREAM*)STDERR_FILENO, "\natexit hook threw an error: "); @@ -270,10 +269,15 @@ JL_DLLEXPORT void jl_atexit_hook(int exitcode) JL_NOTSAFEPOINT_ENTER } JL_GC_POP(); } + ct->world_age = last_age; } - if (ct && exitcode == 0) + if (ct && exitcode == 0) { + size_t last_age = ct->world_age; + ct->world_age = jl_get_world_counter(); jl_write_compiler_output(); + ct->world_age = last_age; + } jl_print_gc_stats(JL_STDERR); if (jl_options.code_coverage) @@ -893,6 +897,7 @@ static NOINLINE void _finish_julia_init(JL_IMAGE_SEARCH rel, jl_ptls_t ptls, jl_ jl_init_primitives(); jl_init_main_module(); jl_load(jl_core_module, "boot.jl"); + jl_current_task->world_age = jl_atomic_load_acquire(&jl_world_counter); post_boot_hooks(); } diff --git a/src/jlapi.c b/src/jlapi.c index defb2db6ac911..b8fbda801f43b 100644 --- a/src/jlapi.c +++ b/src/jlapi.c @@ -910,26 +910,29 @@ static NOINLINE int true_main(int argc, char *argv[]) { jl_set_ARGS(argc, argv); + + jl_task_t *ct = jl_current_task; + size_t last_age = ct->world_age; + ct->world_age = jl_get_world_counter(); + jl_function_t *start_client = jl_base_module ? (jl_function_t*)jl_get_global(jl_base_module, jl_symbol("_start")) : NULL; - jl_task_t *ct = jl_current_task; if (start_client) { int ret = 1; JL_TRY { - size_t last_age = ct->world_age; - ct->world_age = jl_get_world_counter(); jl_value_t *r = jl_apply(&start_client, 1); if (jl_typeof(r) != (jl_value_t*)jl_int32_type) jl_type_error("typeassert", (jl_value_t*)jl_int32_type, r); ret = jl_unbox_int32(r); - ct->world_age = last_age; } JL_CATCH { jl_no_exc_handler(jl_current_exception(ct), ct); } + ct->world_age = last_age; return ret; } + ct->world_age = last_age; // run program if specified, otherwise enter REPL if (argc > 0) { diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index 7fd2dc7409c0e..57f67755df692 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -433,7 +433,7 @@ (inert ,loc))) ,body)))) (if (or (symbol? name) (globalref? name)) - `(block ,@generator (method ,name) ,mdef (unnecessary ,name)) ;; return the function + `(block ,@generator (method ,name) (latestworld-if-toplevel) ,mdef (unnecessary ,name)) ;; return the function (if (not (null? generator)) `(block ,@generator ,mdef) mdef)))))) @@ -531,6 +531,7 @@ `(call (core ifelse) (false) (false) (block ;; forward-declare function so its type can occur in the signature of the inner method below ,@(if (or (symbol? name) (globalref? name)) `((method ,name)) '()) + (latestworld-if-toplevel) ;; call with keyword args pre-sorted - original method code goes here ,(method-def-expr- @@ -1515,6 +1516,7 @@ (scope-block (block (hardscope) (local (= ,(cadr arg) ,rr)) ,.(map (lambda (v) `(,(car e) (globalref (thismodule) ,v) ,v)) (filter-not-underscore (lhs-vars (cadr arg)))) + (latestworld) ,rr)))))))) (else (error "expected assignment after \"const\""))))))) @@ -2473,7 +2475,7 @@ (error "Opaque closure argument type may not be specified both in the method signature and separately")) (if (or (varargexpr? lastarg) (vararg? lastarg)) '(true) '(false)))) - (meth (caddr (caddr (expand-forms F)))) ;; `method` expr + (meth (cadddr (caddr (expand-forms F)))) ;; `method` expr (lam (cadddr meth)) (sig-block (caddr meth)) (sig-block (if (and (pair? sig-block) (eq? (car sig-block) 'block)) @@ -3154,6 +3156,11 @@ (else `(globalref (thismodule) ,e))))) ((or (not (pair? e)) (quoted? e) (memq (car e) '(toplevel symbolicgoto symboliclabel toplevel-only))) e) + ((eq? (car e) 'isglobal) + (let ((val (and scope (get (scope:table scope) (cadr e) #f)))) + (cond (val `(false)) + ((underscore-symbol? (cadr e)) `(false)) + (else `(true))))) ((eq? (car e) 'global) (check-valid-name (cadr e)) e) @@ -3793,7 +3800,7 @@ f(x) = yt(x) (Set '(quote top core lineinfo line inert local-def unnecessary copyast meta inbounds boundscheck loopinfo decl aliasscope popaliasscope thunk with-static-parameters toplevel-only - global globalref assign-const-if-global thismodule + global globalref assign-const-if-global isglobal thismodule const atomic null true false ssavalue isdefined toplevel module lambda error gc_preserve_begin gc_preserve_end import using export public inline noinline purity))) @@ -4296,6 +4303,7 @@ f(x) = yt(x) `(toplevel-butfirst ,(convert-assignment name mk-closure fname lam interp opaq parsed-method-stack globals locals) ,@typedef + (latestworld) ,@(map (lambda (v) `(moved-local ,v)) moved-vars) ,@sp-inits ,@mk-method diff --git a/src/julia.h b/src/julia.h index b5416568b7ae9..699b3cc196411 100644 --- a/src/julia.h +++ b/src/julia.h @@ -2009,7 +2009,6 @@ JL_DLLEXPORT int jl_defines_or_exports_p(jl_module_t *m, jl_sym_t *var); JL_DLLEXPORT int jl_binding_resolved_p(jl_module_t *m, jl_sym_t *var); JL_DLLEXPORT int jl_is_const(jl_module_t *m, jl_sym_t *var); JL_DLLEXPORT int jl_globalref_is_const(jl_globalref_t *gr); -JL_DLLEXPORT int jl_globalref_boundp(jl_globalref_t *gr); JL_DLLEXPORT jl_value_t *jl_get_globalref_value(jl_globalref_t *gr); JL_DLLEXPORT jl_value_t *jl_get_global(jl_module_t *m JL_PROPAGATES_ROOT, jl_sym_t *var); JL_DLLEXPORT void jl_set_global(jl_module_t *m JL_ROOTING_ARGUMENT, jl_sym_t *var, jl_value_t *val JL_ROOTED_ARGUMENT); diff --git a/src/julia_internal.h b/src/julia_internal.h index 3e4967c9d4dca..0da6d412c8a49 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -828,8 +828,8 @@ jl_value_t *modify_value(jl_value_t *ty, _Atomic(jl_value_t*) *p, jl_value_t *pa jl_value_t *modify_bits(jl_value_t *ty, char *p, uint8_t *psel, jl_value_t *parent, jl_value_t *op, jl_value_t *rhs, enum atomic_kind isatomic); int setonce_bits(jl_datatype_t *rty, char *p, jl_value_t *owner, jl_value_t *rhs, enum atomic_kind isatomic); jl_expr_t *jl_exprn(jl_sym_t *head, size_t n); -jl_function_t *jl_new_generic_function(jl_sym_t *name, jl_module_t *module); -jl_function_t *jl_new_generic_function_with_supertype(jl_sym_t *name, jl_module_t *module, jl_datatype_t *st); +jl_function_t *jl_new_generic_function(jl_sym_t *name, jl_module_t *module, size_t new_world); +jl_function_t *jl_new_generic_function_with_supertype(jl_sym_t *name, jl_module_t *module, jl_datatype_t *st, size_t new_world); int jl_foreach_reachable_mtable(int (*visit)(jl_methtable_t *mt, void *env), void *env); int foreach_mtable_in_module(jl_module_t *m, int (*visit)(jl_methtable_t *mt, void *env), void *env); void jl_init_main_module(void); @@ -842,6 +842,7 @@ JL_DLLEXPORT void jl_eval_const_decl(jl_module_t *m, jl_value_t *arg, jl_value_t void jl_binding_set_type(jl_binding_t *b, jl_module_t *mod, jl_sym_t *sym, jl_value_t *ty); void jl_eval_global_expr(jl_module_t *m, jl_expr_t *ex, int set_type); JL_DLLEXPORT void jl_declare_global(jl_module_t *m, jl_value_t *arg, jl_value_t *set_type); +JL_DLLEXPORT jl_binding_partition_t *jl_declare_constant_val3(jl_binding_t *b JL_ROOTING_ARGUMENT, jl_module_t *mod, jl_sym_t *var, jl_value_t *val JL_ROOTED_ARGUMENT JL_MAYBE_UNROOTED, enum jl_partition_kind, size_t new_world) JL_GLOBALLY_ROOTED; JL_DLLEXPORT jl_value_t *jl_toplevel_eval_flex(jl_module_t *m, jl_value_t *e, int fast, int expanded, const char **toplevel_filename, int *toplevel_lineno); STATIC_INLINE struct _jl_module_using *module_usings_getidx(jl_module_t *m JL_PROPAGATES_ROOT, size_t i) JL_NOTSAFEPOINT; @@ -985,15 +986,15 @@ STATIC_INLINE int jl_bkind_is_some_guard(enum jl_partition_kind kind) JL_NOTSAFE return kind == BINDING_KIND_FAILED || kind == BINDING_KIND_GUARD || kind == BINDING_KIND_DECLARED; } -JL_DLLEXPORT jl_binding_partition_t *jl_get_binding_partition(jl_binding_t *b JL_PROPAGATES_ROOT, size_t world); -JL_DLLEXPORT jl_binding_partition_t *jl_get_binding_partition_all(jl_binding_t *b JL_PROPAGATES_ROOT, size_t min_world, size_t max_world); +JL_DLLEXPORT jl_binding_partition_t *jl_get_binding_partition(jl_binding_t *b JL_PROPAGATES_ROOT, size_t world) JL_GLOBALLY_ROOTED; +JL_DLLEXPORT jl_binding_partition_t *jl_get_binding_partition_all(jl_binding_t *b JL_PROPAGATES_ROOT, size_t min_world, size_t max_world) JL_GLOBALLY_ROOTED; EXTERN_INLINE_DECLARE uint8_t jl_bpart_get_kind(jl_binding_partition_t *bpart) JL_NOTSAFEPOINT { return decode_restriction_kind(jl_atomic_load_relaxed(&bpart->restriction)); } -STATIC_INLINE jl_ptr_kind_union_t jl_walk_binding_inplace(jl_binding_t **bnd, jl_binding_partition_t **bpart, size_t world) JL_NOTSAFEPOINT; -STATIC_INLINE jl_ptr_kind_union_t jl_walk_binding_inplace_all(jl_binding_t **bnd, jl_binding_partition_t **bpart, size_t min_world, size_t max_world) JL_NOTSAFEPOINT; +STATIC_INLINE jl_ptr_kind_union_t jl_walk_binding_inplace(jl_binding_t **bnd, jl_binding_partition_t **bpart JL_PROPAGATES_ROOT, size_t world) JL_NOTSAFEPOINT; +STATIC_INLINE jl_ptr_kind_union_t jl_walk_binding_inplace_all(jl_binding_t **bnd, jl_binding_partition_t **bpart JL_PROPAGATES_ROOT, size_t min_world, size_t max_world) JL_NOTSAFEPOINT; #ifndef __clang_analyzer__ STATIC_INLINE jl_ptr_kind_union_t jl_walk_binding_inplace(jl_binding_t **bnd, jl_binding_partition_t **bpart, size_t world) JL_NOTSAFEPOINT @@ -1669,6 +1670,7 @@ JL_DLLEXPORT void jl_set_next_task(jl_task_t *task) JL_NOTSAFEPOINT; // -- synchronization utilities -- // extern jl_mutex_t typecache_lock; +extern jl_mutex_t world_counter_lock; #if defined(__APPLE__) void jl_mach_gc_end(void) JL_NOTSAFEPOINT; diff --git a/src/method.c b/src/method.c index 0a58f0d5c482c..4b39de9aa67e1 100644 --- a/src/method.c +++ b/src/method.c @@ -1073,16 +1073,28 @@ JL_DLLEXPORT void jl_check_gf(jl_value_t *gf, jl_sym_t *name) JL_DLLEXPORT jl_value_t *jl_declare_const_gf(jl_binding_t *b, jl_module_t *mod, jl_sym_t *name) { - jl_value_t *gf = jl_get_binding_value_if_const(b); - if (gf) { - jl_check_gf(gf, b->globalref->name); - return gf; - } - jl_binding_partition_t *bpart = jl_get_binding_partition(b, jl_current_task->world_age); - if (!jl_bkind_is_some_guard(decode_restriction_kind(jl_atomic_load_relaxed(&bpart->restriction)))) + JL_LOCK(&world_counter_lock); + size_t new_world = jl_atomic_load_relaxed(&jl_world_counter) + 1; + jl_binding_partition_t *bpart = jl_get_binding_partition(b, new_world); + jl_ptr_kind_union_t pku = jl_walk_binding_inplace(&b, &bpart, new_world); + jl_value_t *gf = NULL; + if (!jl_bkind_is_some_guard(decode_restriction_kind(pku))) { + if (jl_bkind_is_some_constant(decode_restriction_kind(pku))) { + gf = decode_restriction_value(pku); + JL_GC_PROMISE_ROOTED(gf); + jl_check_gf(gf, b->globalref->name); + JL_UNLOCK(&world_counter_lock); + return gf; + } jl_errorf("cannot define function %s; it already has a value", jl_symbol_name(name)); - gf = (jl_value_t*)jl_new_generic_function(name, mod); - jl_declare_constant_val(b, mod, name, gf); + } + gf = (jl_value_t*)jl_new_generic_function(name, mod, new_world); + // From this point on (if we didn't error), we're committed to raising the world age, + // because we've used it to declare the type name. + jl_atomic_store_release(&jl_world_counter, new_world); + jl_declare_constant_val3(b, mod, name, gf, BINDING_KIND_CONST, new_world); + JL_GC_PROMISE_ROOTED(gf); + JL_UNLOCK(&world_counter_lock); return gf; } diff --git a/src/module.c b/src/module.c index 66049031f8790..be6779727bfdc 100644 --- a/src/module.c +++ b/src/module.c @@ -411,13 +411,13 @@ typedef struct _modstack_t { jl_sym_t *var; struct _modstack_t *prev; } modstack_t; -static jl_binding_t *jl_resolve_owner(jl_binding_t *b/*optional*/, jl_module_t *m JL_PROPAGATES_ROOT, jl_sym_t *var, modstack_t *st); +static jl_binding_t *jl_resolve_owner(jl_binding_t *b/*optional*/, jl_module_t *m JL_PROPAGATES_ROOT, jl_sym_t *var, modstack_t *st, size_t world); JL_DLLEXPORT jl_value_t *jl_reresolve_binding_value_seqcst(jl_binding_t *b) { jl_binding_partition_t *bpart = jl_get_binding_partition(b, jl_current_task->world_age); if (jl_bkind_is_some_guard(decode_restriction_kind(jl_atomic_load_relaxed(&bpart->restriction)))) { - jl_resolve_owner(b, b->globalref->mod, b->globalref->name, NULL); + jl_resolve_owner(b, b->globalref->mod, b->globalref->name, NULL, jl_current_task->world_age); } return jl_get_binding_value_seqcst(b); } @@ -473,7 +473,7 @@ static int eq_bindings(jl_binding_partition_t *owner, jl_binding_t *alias, size_ } // find a binding from a module's `usings` list -static jl_binding_t *using_resolve_binding(jl_module_t *m JL_PROPAGATES_ROOT, jl_sym_t *var, jl_module_t **from, modstack_t *st, int warn) +static jl_binding_t *using_resolve_binding(jl_module_t *m JL_PROPAGATES_ROOT, jl_sym_t *var, jl_module_t **from, modstack_t *st, int warn, size_t world) { jl_binding_t *b = NULL; jl_binding_partition_t *bpart = NULL; @@ -487,20 +487,20 @@ static jl_binding_t *using_resolve_binding(jl_module_t *m JL_PROPAGATES_ROOT, jl JL_UNLOCK(&m->lock); jl_binding_t *tempb = jl_get_module_binding(imp, var, 0); if (tempb != NULL && tempb->exportp) { - tempb = jl_resolve_owner(NULL, imp, var, st); // find the owner for tempb + tempb = jl_resolve_owner(NULL, imp, var, st, world); // find the owner for tempb if (tempb == NULL) // couldn't resolve; try next using (see issue #6105) continue; - jl_binding_partition_t *tempbpart = jl_get_binding_partition(tempb, jl_current_task->world_age); + jl_binding_partition_t *tempbpart = jl_get_binding_partition(tempb, world); jl_ptr_kind_union_t tempb_pku = jl_atomic_load_relaxed(&tempbpart->restriction); - assert(decode_restriction_kind(tempb_pku) == BINDING_KIND_GLOBAL || decode_restriction_kind(tempb_pku) == BINDING_KIND_DECLARED || jl_bkind_is_some_constant(decode_restriction_kind(tempb_pku))); + assert(jl_bkind_is_some_guard(decode_restriction_kind(tempb_pku)) || decode_restriction_kind(tempb_pku) == BINDING_KIND_GLOBAL || decode_restriction_kind(tempb_pku) == BINDING_KIND_DECLARED || jl_bkind_is_some_constant(decode_restriction_kind(tempb_pku))); (void)tempb_pku; - if (bpart != NULL && !tempb->deprecated && !b->deprecated && !eq_bindings(tempbpart, b, jl_current_task->world_age)) { + if (bpart != NULL && !tempb->deprecated && !b->deprecated && !eq_bindings(tempbpart, b, world)) { if (warn) { // set usingfailed=1 to avoid repeating this warning // the owner will still be NULL, so it can be later imported or defined tempb = jl_get_module_binding(m, var, 1); - tempbpart = jl_get_binding_partition(tempb, jl_current_task->world_age); + tempbpart = jl_get_binding_partition(tempb, world); jl_atomic_store_release(&tempbpart->restriction, encode_restriction(NULL, BINDING_KIND_FAILED)); } return NULL; @@ -524,7 +524,7 @@ static jl_module_t *jl_binding_dbgmodule(jl_binding_t *b, jl_module_t *m, jl_sym if (decode_restriction_kind(jl_atomic_load_relaxed(&bpart->restriction)) != BINDING_KIND_GLOBAL) { // for implicitly imported globals, try to re-resolve it to find the module we got it from most directly jl_module_t *from = NULL; - jl_binding_t *b2 = using_resolve_binding(m, var, &from, NULL, 0); + jl_binding_t *b2 = using_resolve_binding(m, var, &from, NULL, 0, jl_current_task->world_age); if (b2) { jl_binding_partition_t *b2part = jl_get_binding_partition(b2, jl_current_task->world_age); if (eq_bindings(b2part, b, jl_current_task->world_age)) @@ -538,11 +538,11 @@ static jl_module_t *jl_binding_dbgmodule(jl_binding_t *b, jl_module_t *m, jl_sym static void jl_binding_dep_message(jl_module_t *m, jl_sym_t *name, jl_binding_t *b); // get binding for reading. might return NULL for unbound. -static jl_binding_t *jl_resolve_owner(jl_binding_t *b/*optional*/, jl_module_t *m, jl_sym_t *var, modstack_t *st) +static jl_binding_t *jl_resolve_owner(jl_binding_t *b/*optional*/, jl_module_t *m, jl_sym_t *var, modstack_t *st, size_t world) { if (b == NULL) b = jl_get_module_binding(m, var, 1); - jl_binding_partition_t *bpart = jl_get_binding_partition(b, jl_current_task->world_age); + jl_binding_partition_t *bpart = jl_get_binding_partition(b, world); jl_ptr_kind_union_t pku = jl_atomic_load_relaxed(&bpart->restriction); retry: if (decode_restriction_kind(pku) == BINDING_KIND_FAILED) @@ -561,7 +561,7 @@ static jl_binding_t *jl_resolve_owner(jl_binding_t *b/*optional*/, jl_module_t * } } jl_module_t *from = NULL; // for error message printing - b2 = using_resolve_binding(m, var, &from, &top, 1); + b2 = using_resolve_binding(m, var, &from, &top, 1, world); if (b2 == NULL) return NULL; assert(from); @@ -594,7 +594,7 @@ static jl_binding_t *jl_resolve_owner(jl_binding_t *b/*optional*/, jl_module_t * } return b2; } - jl_walk_binding_inplace(&b, &bpart, jl_current_task->world_age); + jl_walk_binding_inplace(&b, &bpart, world); return b; } @@ -606,7 +606,7 @@ JL_DLLEXPORT jl_binding_t *jl_binding_owner(jl_module_t *m, jl_sym_t *var) jl_module_t *from = m; jl_ptr_kind_union_t pku = jl_atomic_load_relaxed(&bpart->restriction); if (decode_restriction_kind(pku) == BINDING_KIND_GUARD) { - b = using_resolve_binding(m, var, &from, NULL, 0); + b = using_resolve_binding(m, var, &from, NULL, 0, jl_current_task->world_age); bpart = jl_get_binding_partition(b, jl_current_task->world_age); } pku = jl_walk_binding_inplace(&b, &bpart, jl_current_task->world_age); @@ -637,7 +637,7 @@ JL_DLLEXPORT jl_value_t *jl_get_binding_type(jl_module_t *m, jl_sym_t *var) JL_DLLEXPORT jl_binding_t *jl_get_binding(jl_module_t *m, jl_sym_t *var) { - return jl_resolve_owner(NULL, m, var, NULL); + return jl_resolve_owner(NULL, m, var, NULL, jl_current_task->world_age); } JL_DLLEXPORT jl_binding_t *jl_get_binding_or_error(jl_module_t *m, jl_sym_t *var) @@ -1001,7 +1001,7 @@ JL_DLLEXPORT jl_binding_t *jl_get_module_binding(jl_module_t *m, jl_sym_t *var, JL_DLLEXPORT jl_value_t *jl_get_globalref_value(jl_globalref_t *gr) { jl_binding_t *b = gr->binding; - b = jl_resolve_owner(b, gr->mod, gr->name, NULL); + b = jl_resolve_owner(b, gr->mod, gr->name, NULL, jl_current_task->world_age); // ignores b->deprecated return b == NULL ? NULL : jl_get_binding_value(b); } @@ -1028,6 +1028,7 @@ JL_DLLEXPORT void jl_set_const(jl_module_t *m JL_ROOTING_ARGUMENT, jl_sym_t *var // this function is mostly only used during initialization, so the data races here are not too important to us jl_binding_t *bp = jl_get_module_binding(m, var, 1); jl_binding_partition_t *bpart = jl_get_binding_partition(bp, jl_current_task->world_age); + assert(jl_bkind_is_some_guard(decode_restriction_kind(jl_atomic_load_relaxed(&bpart->restriction)))); jl_atomic_store_release(&bpart->restriction, encode_restriction(val, BINDING_KIND_CONST)); jl_gc_wb(bpart, val); } @@ -1047,11 +1048,10 @@ void jl_invalidate_binding_refs(jl_globalref_t *ref, jl_binding_partition_t *inv JL_GC_POP(); } -extern jl_mutex_t world_counter_lock; JL_DLLEXPORT void jl_disable_binding(jl_globalref_t *gr) { jl_binding_t *b = gr->binding; - b = jl_resolve_owner(b, gr->mod, gr->name, NULL); + b = jl_resolve_owner(b, gr->mod, gr->name, NULL, jl_current_task->world_age); jl_binding_partition_t *bpart = jl_get_binding_partition(b, jl_current_task->world_age); if (decode_restriction_kind(jl_atomic_load_relaxed(&bpart->restriction)) == BINDING_KIND_GUARD) { @@ -1074,18 +1074,17 @@ JL_DLLEXPORT void jl_disable_binding(jl_globalref_t *gr) JL_DLLEXPORT int jl_globalref_is_const(jl_globalref_t *gr) { jl_binding_t *b = gr->binding; - b = jl_resolve_owner(b, gr->mod, gr->name, NULL); + b = jl_resolve_owner(b, gr->mod, gr->name, NULL, jl_current_task->world_age); jl_binding_partition_t *bpart = jl_get_binding_partition(b, jl_current_task->world_age); if (!bpart) return 0; return jl_bkind_is_some_constant(decode_restriction_kind(jl_atomic_load_relaxed(&bpart->restriction))); } -JL_DLLEXPORT int jl_globalref_boundp(jl_globalref_t *gr) +JL_DLLEXPORT void jl_force_binding_resolution(jl_globalref_t *gr, size_t world) { jl_binding_t *b = gr->binding; - b = jl_resolve_owner(b, gr->mod, gr->name, NULL); - return b && jl_get_binding_value(b) != NULL; + jl_resolve_owner(b, gr->mod, gr->name, NULL, world); } JL_DLLEXPORT int jl_is_const(jl_module_t *m, jl_sym_t *var) diff --git a/src/staticdata.c b/src/staticdata.c index 7fad87652b26a..ff352cd8c152f 100644 --- a/src/staticdata.c +++ b/src/staticdata.c @@ -1674,8 +1674,18 @@ static void jl_write_values(jl_serializer_state *s) JL_GC_DISABLED #ifndef _P64 write_uint(f, decode_restriction_kind(pku)); #endif - write_uint(f, bpart->min_world); - write_uint(f, jl_atomic_load_relaxed(&bpart->max_world)); + size_t max_world = jl_atomic_load_relaxed(&bpart->max_world); + if (max_world == ~(size_t)0) { + // Still valid. Will be considered primordial after re-load. + // We could consider updating min_world to the loaded world, but + // there doesn't appear to be much point. + write_uint(f, 0); + write_uint(f, max_world); + } else { + // The world will not be reachable after loading + write_uint(f, 1); + write_uint(f, 0); + } write_pointerfield(s, (jl_value_t*)jl_atomic_load_relaxed(&bpart->next)); #ifdef _P64 write_uint(f, decode_restriction_kind(pku)); // This will be moved back into place during deserialization (if necessary) @@ -2714,7 +2724,6 @@ static void jl_strip_all_codeinfos(void) jl_genericmemory_t *jl_global_roots_list; jl_genericmemory_t *jl_global_roots_keyset; jl_mutex_t global_roots_lock; -extern jl_mutex_t world_counter_lock; jl_mutex_t precompile_field_replace_lock; jl_svec_t *precompile_field_replace JL_GLOBALLY_ROOTED; diff --git a/src/toplevel.c b/src/toplevel.c index fb217ec7cb52e..dee9029e2feb7 100644 --- a/src/toplevel.c +++ b/src/toplevel.c @@ -66,14 +66,13 @@ void jl_module_run_initializer(jl_module_t *m) { JL_TIMING(INIT_MODULE, INIT_MODULE); jl_timing_show_module(m, JL_TIMING_DEFAULT_BLOCK); - jl_function_t *f = jl_module_get_initializer(m); - if (f == NULL) - return; jl_task_t *ct = jl_current_task; size_t last_age = ct->world_age; JL_TRY { ct->world_age = jl_atomic_load_acquire(&jl_world_counter); - jl_apply(&f, 1); + jl_function_t *f = jl_module_get_initializer(m); + if (f != NULL) + jl_apply(&f, 1); ct->world_age = last_age; } JL_CATCH { @@ -740,10 +739,15 @@ static void jl_eval_errorf(jl_module_t *m, const char *filename, int lineno, con JL_GC_POP(); } -JL_DLLEXPORT jl_binding_partition_t *jl_declare_constant_val2(jl_binding_t *b, jl_module_t *mod, jl_sym_t *var, jl_value_t *val, enum jl_partition_kind constant_kind) +JL_DLLEXPORT jl_binding_partition_t *jl_declare_constant_val3( + jl_binding_t *b, jl_module_t *mod, jl_sym_t *var, jl_value_t *val, + enum jl_partition_kind constant_kind, size_t new_world) { JL_GC_PUSH1(&val); - jl_binding_partition_t *bpart = jl_get_binding_partition(b, jl_current_task->world_age); + if (!b) { + b = jl_get_module_binding(mod, var, 1); + } + jl_binding_partition_t *bpart = jl_get_binding_partition(b, new_world); jl_ptr_kind_union_t pku = jl_atomic_load_relaxed(&bpart->restriction); int did_warn = 0; while (1) { @@ -780,10 +784,27 @@ JL_DLLEXPORT jl_binding_partition_t *jl_declare_constant_val2(jl_binding_t *b, j break; } } + // N.B.: This backdates the first definition of the constant to world age 0 for backwards compatibility + // TODO: Mark this specially with a separate partition. + if (bpart->min_world != 0) + bpart->min_world = new_world; JL_GC_POP(); return bpart; } +JL_DLLEXPORT jl_binding_partition_t *jl_declare_constant_val2( + jl_binding_t *b, jl_module_t *mod, jl_sym_t *var, jl_value_t *val, + enum jl_partition_kind constant_kind) +{ + JL_LOCK(&world_counter_lock); + size_t new_world = jl_atomic_load_relaxed(&jl_world_counter) + 1; + jl_binding_partition_t *bpart = jl_declare_constant_val3(b, mod, var, val, constant_kind, new_world); + if (bpart->min_world == new_world) + jl_atomic_store_release(&jl_world_counter, new_world); + JL_UNLOCK(&world_counter_lock); + return bpart; +} + JL_DLLEXPORT jl_binding_partition_t *jl_declare_constant_val(jl_binding_t *b, jl_module_t *mod, jl_sym_t *var, jl_value_t *val) { return jl_declare_constant_val2(b, mod, var, val, val ? BINDING_KIND_CONST : BINDING_KIND_UNDEF_CONST); @@ -869,6 +890,7 @@ JL_DLLEXPORT jl_value_t *jl_toplevel_eval_flex(jl_module_t *JL_NONNULL m, jl_val else if (head == jl_using_sym) { jl_sym_t *name = NULL; jl_module_t *from = eval_import_from(m, ex, "using"); + ct->world_age = jl_atomic_load_acquire(&jl_world_counter); size_t i = 0; if (from) { i = 1; @@ -908,6 +930,7 @@ JL_DLLEXPORT jl_value_t *jl_toplevel_eval_flex(jl_module_t *JL_NONNULL m, jl_val jl_expr_t *path = (jl_expr_t*)jl_exprarg(a, 0); name = NULL; jl_module_t *import = eval_import_path(m, from, ((jl_expr_t*)path)->args, &name, "using"); + ct->world_age = jl_atomic_load_acquire(&jl_world_counter); assert(name); check_macro_rename(name, asname, "using"); // `using A: B as C` syntax @@ -919,11 +942,13 @@ JL_DLLEXPORT jl_value_t *jl_toplevel_eval_flex(jl_module_t *JL_NONNULL m, jl_val "syntax: malformed \"using\" statement"); } JL_GC_POP(); + ct->world_age = last_age; return jl_nothing; } else if (head == jl_import_sym) { jl_sym_t *name = NULL; jl_module_t *from = eval_import_from(m, ex, "import"); + ct->world_age = jl_atomic_load_acquire(&jl_world_counter); size_t i = 0; if (from) { i = 1; @@ -967,6 +992,7 @@ JL_DLLEXPORT jl_value_t *jl_toplevel_eval_flex(jl_module_t *JL_NONNULL m, jl_val "syntax: malformed \"import\" statement"); } JL_GC_POP(); + ct->world_age = last_age; return jl_nothing; } else if (head == jl_export_sym || head == jl_public_sym) { diff --git a/stdlib/REPL/src/REPLCompletions.jl b/stdlib/REPL/src/REPLCompletions.jl index e8aa1188ec213..c63c5a557acd8 100644 --- a/stdlib/REPL/src/REPLCompletions.jl +++ b/stdlib/REPL/src/REPLCompletions.jl @@ -639,18 +639,24 @@ function is_call_graph_uncached(sv::CC.InferenceState) return is_call_graph_uncached(parent::CC.InferenceState) end -isdefined_globalref(g::GlobalRef) = !iszero(ccall(:jl_globalref_boundp, Cint, (Any,), g)) - # aggressive global binding resolution within `repl_frame` function CC.abstract_eval_globalref(interp::REPLInterpreter, g::GlobalRef, bailed::Bool, sv::CC.InferenceState) + # Ignore saw_latestworld + partition = CC.abstract_eval_binding_partition!(interp, g, sv) if (interp.limit_aggressive_inference ? is_repl_frame(sv) : is_call_graph_uncached(sv)) - if isdefined_globalref(g) + if CC.is_defined_const_binding(CC.binding_kind(partition)) return Pair{CC.RTEffects, Union{Nothing, Core.BindingPartition}}( - CC.RTEffects(Const(ccall(:jl_get_globalref_value, Any, (Any,), g)), Union{}, CC.EFFECTS_TOTAL), nothing) + CC.RTEffects(Const(CC.partition_restriction(partition)), Union{}, CC.EFFECTS_TOTAL), partition) + else + b = convert(Core.Binding, g) + if CC.binding_kind(partition) == CC.BINDING_KIND_GLOBAL && isdefined(b, :value) + return Pair{CC.RTEffects, Union{Nothing, Core.BindingPartition}}( + CC.RTEffects(Const(b.value), Union{}, CC.EFFECTS_TOTAL), partition) + end end return Pair{CC.RTEffects, Union{Nothing, Core.BindingPartition}}( - CC.RTEffects(Union{}, UndefVarError, CC.EFFECTS_THROWS), nothing) + CC.RTEffects(Union{}, UndefVarError, CC.EFFECTS_THROWS), partition) end return @invoke CC.abstract_eval_globalref(interp::CC.AbstractInterpreter, g::GlobalRef, bailed::Bool, sv::CC.InferenceState) diff --git a/stdlib/REPL/src/precompile.jl b/stdlib/REPL/src/precompile.jl index daa01f626aeab..4f3b3a6eae083 100644 --- a/stdlib/REPL/src/precompile.jl +++ b/stdlib/REPL/src/precompile.jl @@ -8,6 +8,7 @@ import ..REPL Base._track_dependencies[] = false try Base.include(@__MODULE__, joinpath(Sys.BINDIR, "..", "share", "julia", "test", "testhelpers", "FakePTYs.jl")) + @Core.latestworld import .FakePTYs: open_fake_pty finally Base._track_dependencies[] = true diff --git a/stdlib/REPL/test/precompilation.jl b/stdlib/REPL/test/precompilation.jl index 7efcf0b5e8282..01a062644596c 100644 --- a/stdlib/REPL/test/precompilation.jl +++ b/stdlib/REPL/test/precompilation.jl @@ -33,7 +33,7 @@ if !Sys.iswindows() # given this test checks that startup is snappy, it's best to add workloads to # contrib/generate_precompile.jl rather than increase this number. But if that's not # possible, it'd be helpful to add a comment with the statement and a reason below - expected_precompiles = 0 + expected_precompiles = 1 n_precompiles = count(r"precompile\(", tracecompile_out) diff --git a/stdlib/REPL/test/repl.jl b/stdlib/REPL/test/repl.jl index 809913502c3d7..8944fd76f31de 100644 --- a/stdlib/REPL/test/repl.jl +++ b/stdlib/REPL/test/repl.jl @@ -769,9 +769,9 @@ fake_repl() do stdin_write, stdout_read, repl julia> A = 3\e[201~ """) @test Main.A == 3 - @test Base.invokelatest(Main.foo, 4) - @test Base.invokelatest(Main.T17599, 3).a == 3 - @test !Base.invokelatest(Main.foo, 2) + @test @invokelatest(Main.foo(4)) + @test @invokelatest(Main.T17599(3)).a == 3 + @test !@invokelatest(Main.foo(2)) sendrepl2("""\e[200~ julia> goo(x) = x + 1 @@ -781,7 +781,7 @@ fake_repl() do stdin_write, stdout_read, repl 4\e[201~ """) @test Main.A == 4 - @test Base.invokelatest(Main.goo, 4) == 5 + @test @invokelatest(Main.goo(4)) == 5 # Test prefix removal only active in bracket paste mode sendrepl2("julia = 4\n julia> 3 && (A = 1)\n") diff --git a/stdlib/Serialization/test/runtests.jl b/stdlib/Serialization/test/runtests.jl index 4d9b439e639d7..f1b83ca947c7e 100644 --- a/stdlib/Serialization/test/runtests.jl +++ b/stdlib/Serialization/test/runtests.jl @@ -130,7 +130,7 @@ create_serialization_stream() do s # user-defined module modtype = eval(Meta.parse("$(modstring)")) serialize(s, modtype) seek(s, 0) - @test deserialize(s) === modtype + @test invokelatest(deserialize, s) === modtype end # DataType @@ -151,7 +151,7 @@ create_serialization_stream() do s # user-defined type utype = eval(Meta.parse("$(usertype)")) serialize(s, utype) seek(s, 0) - @test deserialize(s) === utype + @test invokelatest(deserialize, s) === utype end create_serialization_stream() do s # user-defined type @@ -160,7 +160,7 @@ create_serialization_stream() do s # user-defined type utype = eval(Meta.parse("$(usertype)")) serialize(s, utype) seek(s, 0) - @test deserialize(s) === utype + @test invokelatest(deserialize, s) === utype end create_serialization_stream() do s # user-defined type @@ -169,7 +169,7 @@ create_serialization_stream() do s # user-defined type utype = eval(Meta.parse("$(usertype)")) serialize(s, utype) seek(s, 0) - @test deserialize(s) == utype + @test invokelatest(deserialize, s) == utype end create_serialization_stream() do s # immutable struct with 1 field @@ -178,7 +178,7 @@ create_serialization_stream() do s # immutable struct with 1 field utype = eval(Meta.parse("$(usertype)")) serialize(s, utype) seek(s, 0) - @test deserialize(s) == utype + @test invokelatest(deserialize, s) == utype end create_serialization_stream() do s # immutable struct with 2 field @@ -187,7 +187,7 @@ create_serialization_stream() do s # immutable struct with 2 field utval = eval(Meta.parse("$(usertype)(1,2)")) serialize(s, utval) seek(s, 0) - @test deserialize(s) === utval + @test invokelatest(deserialize, s) === utval end create_serialization_stream() do s # immutable struct with 3 field @@ -196,7 +196,7 @@ create_serialization_stream() do s # immutable struct with 3 field utval = eval(Meta.parse("$(usertype)(1,2,3)")) serialize(s, utval) seek(s, 0) - @test deserialize(s) === utval + @test invokelatest(deserialize, s) === utval end create_serialization_stream() do s # immutable struct with 4 field @@ -205,7 +205,7 @@ create_serialization_stream() do s # immutable struct with 4 field utval = eval(Meta.parse("$(usertype)(1,2,3,4)")) serialize(s, utval) seek(s, 0) - @test deserialize(s) === utval + @test invokelatest(deserialize, s) === utval end # Expression diff --git a/test/core.jl b/test/core.jl index 4bbb2ca368019..3886e6728df10 100644 --- a/test/core.jl +++ b/test/core.jl @@ -2622,7 +2622,7 @@ end # issue #8338 let ex = Expr(:(=), :(f8338(x;y=4)), :(x*y)) eval(ex) - @test invokelatest(f8338, 2) == 8 + @test (@invokelatest f8338(2)) == 8 end # call overloading (#2403) diff --git a/test/loading.jl b/test/loading.jl index 09f96e1f43578..be8f08b4bfe22 100644 --- a/test/loading.jl +++ b/test/loading.jl @@ -798,6 +798,7 @@ end @testset "`::AbstractString` constraint on the path argument to `include`" begin for m ∈ (NotPkgModule, evalfile("testhelpers/just_module.jl")) + @Core.latestworld let i = m.include @test !applicable(i, (nothing,)) @test !applicable(i, (identity, nothing,)) diff --git a/test/precompile.jl b/test/precompile.jl index 55e97f5608b08..f5a412b416ddc 100644 --- a/test/precompile.jl +++ b/test/precompile.jl @@ -610,13 +610,17 @@ precompile_test_harness(false) do dir @eval using UseBaz @test haskey(Base.loaded_modules, Base.PkgId("UseBaz")) @test haskey(Base.loaded_modules, Base.PkgId("Baz")) - @test Base.invokelatest(UseBaz.biz) === 1 - @test Base.invokelatest(UseBaz.buz) === 2 - @test UseBaz.generating == 0 - @test UseBaz.incremental == 0 + invokelatest() do + @test UseBaz.biz() === 1 + @test UseBaz.buz() === 2 + @test UseBaz.generating == 0 + @test UseBaz.incremental == 0 + end @eval using Baz - @test Base.invokelatest(Baz.baz) === 1 - @test Baz === UseBaz.Baz + invokelatest() do + @test Baz.baz() === 1 + @test Baz === UseBaz.Baz + end # should not throw if the cachefile does not exist @test !isfile("DoesNotExist.ji") diff --git a/test/runtests.jl b/test/runtests.jl index fd0326d48ee6c..f0c5e1b94c376 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -112,7 +112,7 @@ cd(@__DIR__) do @everywhere include("testdefs.jl") if use_revise - Base.invokelatest(revise_trackall) + @invokelatest revise_trackall() Distributed.remotecall_eval(Main, workers(), revise_init_expr) end @@ -250,7 +250,7 @@ cd(@__DIR__) do wrkr = p before = time() resp, duration = try - r = remotecall_fetch(runtests, wrkr, test, test_path(test); seed=seed) + r = remotecall_fetch(@Base.world(runtests, ∞), wrkr, test, test_path(test); seed=seed) r, time() - before catch e isa(e, InterruptException) && return @@ -310,7 +310,7 @@ cd(@__DIR__) do t == "SharedArrays" && (isolate = false) before = time() resp, duration = try - r = Base.invokelatest(runtests, t, test_path(t), isolate, seed=seed) # runtests is defined by the include above + r = @invokelatest runtests(t, test_path(t), isolate, seed=seed) # runtests is defined by the include above r, time() - before catch e isa(e, InterruptException) && rethrow() diff --git a/test/syntax.jl b/test/syntax.jl index bdf9579b134eb..aaeeea7aec161 100644 --- a/test/syntax.jl +++ b/test/syntax.jl @@ -3303,6 +3303,7 @@ const typeof = error end let ex = :(const $(esc(:x)) = 1; (::typeof($(esc(:foo43993))))() = $(esc(:x))) Core.eval(M43993, Expr(:var"hygienic-scope", ex, Core)) + @Core.latestworld @test M43993.x === 1 @test invokelatest(M43993.foo43993) === 1 end diff --git a/test/worlds.jl b/test/worlds.jl index 268a6664571fb..8bc96f8303aef 100644 --- a/test/worlds.jl +++ b/test/worlds.jl @@ -115,14 +115,14 @@ wc265_41332a = Task(tls_world_age) global wc265_41332d = Task(tls_world_age) nothing end)() -@test wc265 + 2 == get_world_counter() == tls_world_age() +@test wc265 + 3 == get_world_counter() == tls_world_age() schedule(wc265_41332a) schedule(wc265_41332b) schedule(wc265_41332c) schedule(wc265_41332d) @test wc265 == fetch(wc265_41332a) @test wc265 + 1 == fetch(wc265_41332b) -@test wc265 + 2 == fetch(wc265_41332c) +@test wc265 + 3 == fetch(wc265_41332c) @test wc265 + 1 == fetch(wc265_41332d) chnls, tasks = Base.channeled_tasks(2, wfunc) t265 = tasks[1] From 61e8f1d3e561e480a05285745239c0df66f111ef Mon Sep 17 00:00:00 2001 From: Max Horn Date: Wed, 22 Jan 2025 14:55:49 +0100 Subject: [PATCH 16/56] Move `jl_task_t` to `julia_threads.h` (#57117) This is a subset of PR #56477 . I hope that it will be easier to get this merged first, as it just moves things around, afterwards we can update #56477 and it will be considerably smaller, and will thus hopefully break less often (and in less bad ways) over time. (I can perform the required update of that PR, too). See also PR #57116 which contains another independent part of that PR. --- src/julia.h | 85 +++++---------------------------------------- src/julia_threads.h | 70 +++++++++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+), 77 deletions(-) diff --git a/src/julia.h b/src/julia.h index 699b3cc196411..4c699ba059c65 100644 --- a/src/julia.h +++ b/src/julia.h @@ -72,21 +72,20 @@ typedef struct _jl_tls_states_t *jl_ptls_t; #endif #include "gc-interface.h" #include "julia_atomics.h" -#include "julia_threads.h" #include "julia_assert.h" +// the common fields are hidden before the pointer, but the following macro is +// used to indicate which types below are subtypes of jl_value_t +#define JL_DATA_TYPE +typedef struct _jl_value_t jl_value_t; +#include "julia_threads.h" + #ifdef __cplusplus extern "C" { #endif // core data types ------------------------------------------------------------ -// the common fields are hidden before the pointer, but the following macro is -// used to indicate which types below are subtypes of jl_value_t -#define JL_DATA_TYPE - -typedef struct _jl_value_t jl_value_t; - struct _jl_taggedvalue_bits { uintptr_t gc:2; uintptr_t in_image:1; @@ -484,9 +483,6 @@ typedef struct _jl_abi_override_t { jl_method_instance_t *def; } jl_abi_override_t; -// all values are callable as Functions -typedef jl_value_t jl_function_t; - typedef struct { JL_DATA_TYPE jl_sym_t *name; @@ -2262,12 +2258,8 @@ JL_DLLEXPORT void jl_sigatomic_end(void); // tasks and exceptions ------------------------------------------------------- -typedef struct _jl_timing_block_t jl_timing_block_t; -typedef struct _jl_timing_event_t jl_timing_event_t; -typedef struct _jl_excstack_t jl_excstack_t; - // info describing an exception handler -typedef struct _jl_handler_t { +struct _jl_handler_t { jl_jmp_buf eh_ctx; jl_gcframe_t *gcstack; jl_value_t *scope; @@ -2277,68 +2269,7 @@ typedef struct _jl_handler_t { sig_atomic_t defer_signal; jl_timing_block_t *timing_stack; size_t world_age; -} jl_handler_t; - -#define JL_RNG_SIZE 5 // xoshiro 4 + splitmix 1 - -typedef struct _jl_task_t { - JL_DATA_TYPE - jl_value_t *next; // invasive linked list for scheduler - jl_value_t *queue; // invasive linked list for scheduler - jl_value_t *tls; - jl_value_t *donenotify; - jl_value_t *result; - jl_value_t *scope; - jl_function_t *start; - _Atomic(uint8_t) _state; - uint8_t sticky; // record whether this Task can be migrated to a new thread - uint16_t priority; - _Atomic(uint8_t) _isexception; // set if `result` is an exception to throw or that we exited with - uint8_t pad0[3]; - // === 64 bytes (cache line) - uint64_t rngState[JL_RNG_SIZE]; - // flag indicating whether or not to record timing metrics for this task - uint8_t metrics_enabled; - uint8_t pad1[3]; - // timestamp this task first entered the run queue - _Atomic(uint64_t) first_enqueued_at; - // timestamp this task was most recently scheduled to run - _Atomic(uint64_t) last_started_running_at; - // time this task has spent running; updated when it yields or finishes. - _Atomic(uint64_t) running_time_ns; - // === 64 bytes (cache line) - // timestamp this task finished (i.e. entered state DONE or FAILED). - _Atomic(uint64_t) finished_at; - -// hidden state: - - // id of owning thread - does not need to be defined until the task runs - _Atomic(int16_t) tid; - // threadpool id - int8_t threadpoolid; - // Reentrancy bits - // Bit 0: 1 if we are currently running inference/codegen - // Bit 1-2: 0-3 counter of how many times we've reentered inference - // Bit 3: 1 if we are writing the image and inference is illegal - uint8_t reentrant_timing; - // 2 bytes of padding on 32-bit, 6 bytes on 64-bit - // uint16_t padding2_32; - // uint48_t padding2_64; - // saved gc stack top for context switches - jl_gcframe_t *gcstack; - size_t world_age; - // quick lookup for current ptls - jl_ptls_t ptls; // == jl_all_tls_states[tid] -#ifdef USE_TRACY - const char *name; -#endif - // saved exception stack - jl_excstack_t *excstack; - // current exception handler - jl_handler_t *eh; - // saved thread state - jl_ucontext_t ctx; // pointer into stkbuf, if suspended -} jl_task_t; +}; #define JL_TASK_STATE_RUNNABLE 0 #define JL_TASK_STATE_DONE 1 diff --git a/src/julia_threads.h b/src/julia_threads.h index b6ef65dc7fe52..061eb9266e7a7 100644 --- a/src/julia_threads.h +++ b/src/julia_threads.h @@ -218,6 +218,76 @@ typedef struct _jl_tls_states_t { #endif } jl_tls_states_t; +#define JL_RNG_SIZE 5 // xoshiro 4 + splitmix 1 + +// all values are callable as Functions +typedef jl_value_t jl_function_t; + +typedef struct _jl_timing_block_t jl_timing_block_t; +typedef struct _jl_timing_event_t jl_timing_event_t; +typedef struct _jl_excstack_t jl_excstack_t; + +typedef struct _jl_handler_t jl_handler_t; + +typedef struct _jl_task_t { + JL_DATA_TYPE + jl_value_t *next; // invasive linked list for scheduler + jl_value_t *queue; // invasive linked list for scheduler + jl_value_t *tls; + jl_value_t *donenotify; + jl_value_t *result; + jl_value_t *scope; + jl_function_t *start; + _Atomic(uint8_t) _state; + uint8_t sticky; // record whether this Task can be migrated to a new thread + uint16_t priority; + _Atomic(uint8_t) _isexception; // set if `result` is an exception to throw or that we exited with + uint8_t pad0[3]; + // === 64 bytes (cache line) + uint64_t rngState[JL_RNG_SIZE]; + // flag indicating whether or not to record timing metrics for this task + uint8_t metrics_enabled; + uint8_t pad1[3]; + // timestamp this task first entered the run queue + _Atomic(uint64_t) first_enqueued_at; + // timestamp this task was most recently scheduled to run + _Atomic(uint64_t) last_started_running_at; + // time this task has spent running; updated when it yields or finishes. + _Atomic(uint64_t) running_time_ns; + // === 64 bytes (cache line) + // timestamp this task finished (i.e. entered state DONE or FAILED). + _Atomic(uint64_t) finished_at; + +// hidden state: + + // id of owning thread - does not need to be defined until the task runs + _Atomic(int16_t) tid; + // threadpool id + int8_t threadpoolid; + // Reentrancy bits + // Bit 0: 1 if we are currently running inference/codegen + // Bit 1-2: 0-3 counter of how many times we've reentered inference + // Bit 3: 1 if we are writing the image and inference is illegal + uint8_t reentrant_timing; + // 2 bytes of padding on 32-bit, 6 bytes on 64-bit + // uint16_t padding2_32; + // uint48_t padding2_64; + // saved gc stack top for context switches + jl_gcframe_t *gcstack; + size_t world_age; + // quick lookup for current ptls + jl_ptls_t ptls; // == jl_all_tls_states[tid] +#ifdef USE_TRACY + const char *name; +#endif + // saved exception stack + jl_excstack_t *excstack; + // current exception handler + jl_handler_t *eh; + // saved thread state + jl_ucontext_t ctx; // pointer into stkbuf, if suspended +} jl_task_t; + JL_DLLEXPORT void *jl_get_ptls_states(void); // Update codegen version in `ccall.cpp` after changing either `pause` or `wake` From eed336505356b51efc0ab6fa0daa46d339878efe Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Wed, 22 Jan 2025 12:51:09 -0500 Subject: [PATCH 17/56] Docs: Update version that version-named manifests works from (#56709) --- doc/src/manual/code-loading.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/src/manual/code-loading.md b/doc/src/manual/code-loading.md index 5c8315693c71e..24e64b0ca068e 100644 --- a/doc/src/manual/code-loading.md +++ b/doc/src/manual/code-loading.md @@ -63,7 +63,7 @@ Each kind of environment defines these three maps differently, as detailed in th ### Project environments -A project environment is determined by a directory containing a project file called `Project.toml`, and optionally a manifest file called `Manifest.toml`. These files may also be called `JuliaProject.toml` and `JuliaManifest.toml`, in which case `Project.toml` and `Manifest.toml` are ignored. This allows for coexistence with other tools that might consider files called `Project.toml` and `Manifest.toml` significant. For pure Julia projects, however, the names `Project.toml` and `Manifest.toml` are preferred. However, from Julia v1.11 onwards, `(Julia)Manifest-v{major}.{minor}.toml` is recognized as a format to make a given julia version use a specific manifest file i.e. in the same folder, a `Manifest-v1.11.toml` would be used by v1.11 and `Manifest.toml` by any other julia version. +A project environment is determined by a directory containing a project file called `Project.toml`, and optionally a manifest file called `Manifest.toml`. These files may also be called `JuliaProject.toml` and `JuliaManifest.toml`, in which case `Project.toml` and `Manifest.toml` are ignored. This allows for coexistence with other tools that might consider files called `Project.toml` and `Manifest.toml` significant. For pure Julia projects, however, the names `Project.toml` and `Manifest.toml` are preferred. However, from Julia v1.10.8 onwards, `(Julia)Manifest-v{major}.{minor}.toml` is recognized as a format to make a given julia version use a specific manifest file i.e. in the same folder, a `Manifest-v1.11.toml` would be used by v1.11 and `Manifest.toml` by any other julia version. The roots, graph and paths maps of a project environment are defined as follows: From 090e291eba1b7668bfc5234215750b4bd80f5387 Mon Sep 17 00:00:00 2001 From: Erik Schnetter Date: Wed, 22 Jan 2025 17:30:00 -0500 Subject: [PATCH 18/56] runtime_intrinsics.c: Correct `max_double` (#57124) Closes #57119. --- src/runtime_intrinsics.c | 8 ++--- test/intrinsics.jl | 78 ++++++++++++++++++++++++++++++++++++++-- 2 files changed, 80 insertions(+), 6 deletions(-) diff --git a/src/runtime_intrinsics.c b/src/runtime_intrinsics.c index 90afa3fb6bddf..49d510cc48c34 100644 --- a/src/runtime_intrinsics.c +++ b/src/runtime_intrinsics.c @@ -1424,17 +1424,17 @@ bi_fintrinsic(_min, min_float) float max_float(float x, float y) JL_NOTSAFEPOINT { float diff = x - y; - float argmin = signbit(diff) ? y : x; + float argmax = signbit(diff) ? y : x; int is_nan = isnan(x) || isnan(y); - return is_nan ? diff : argmin; + return is_nan ? diff : argmax; } double max_double(double x, double y) JL_NOTSAFEPOINT { double diff = x - y; - double argmin = signbit(diff) ? x : y; + double argmax = signbit(diff) ? y : x; int is_nan = isnan(x) || isnan(y); - return is_nan ? diff : argmin; + return is_nan ? diff : argmax; } #define _max(a, b) sizeof(a) == sizeof(float) ? max_float(a, b) : max_double(a, b) diff --git a/test/intrinsics.jl b/test/intrinsics.jl index 7a63cd1c0a62e..bc1838ce2c68b 100644 --- a/test/intrinsics.jl +++ b/test/intrinsics.jl @@ -147,9 +147,81 @@ macro test_intrinsic(intr, args...) end end +@testset "Float64 intrinsics" begin + # unary + @test_intrinsic Core.Intrinsics.abs_float Float64(-3.3) Float64(3.3) + @test_intrinsic Core.Intrinsics.neg_float Float64(3.3) Float64(-3.3) + @test_intrinsic Core.Intrinsics.fpext Float64 Float64(3.3) Float64(3.3) + + # binary + @test_intrinsic Core.Intrinsics.add_float Float64(3.3) Float64(2) Float64(5.3) + @test_intrinsic Core.Intrinsics.sub_float Float64(3.3) Float64(2) Float64(1.2999999999999998) + @test_intrinsic Core.Intrinsics.mul_float Float64(3.3) Float64(2) Float64(6.6) + @test_intrinsic Core.Intrinsics.div_float Float64(3.3) Float64(2) Float64(1.65) + @test_intrinsic Core.Intrinsics.max_float Float64(1.0) Float64(2.0) Float64(2.0) + @test_intrinsic Core.Intrinsics.min_float Float64(1.0) Float64(2.0) Float64(1.0) + + # ternary + @test_intrinsic Core.Intrinsics.fma_float Float64(3.3) Float64(4.4) Float64(5.5) Float64(20.02) + @test_intrinsic Core.Intrinsics.muladd_float Float64(3.3) Float64(4.4) Float64(5.5) Float64(20.02) + + # boolean + @test_intrinsic Core.Intrinsics.eq_float Float64(3.3) Float64(3.3) true + @test_intrinsic Core.Intrinsics.eq_float Float64(3.3) Float64(2) false + @test_intrinsic Core.Intrinsics.ne_float Float64(3.3) Float64(3.3) false + @test_intrinsic Core.Intrinsics.ne_float Float64(3.3) Float64(2) true + @test_intrinsic Core.Intrinsics.le_float Float64(3.3) Float64(3.3) true + @test_intrinsic Core.Intrinsics.le_float Float64(3.3) Float64(2) false + + # conversions + @test_intrinsic Core.Intrinsics.sitofp Float64 3 Float64(3.0) + @test_intrinsic Core.Intrinsics.uitofp Float64 UInt(3) Float64(3.0) + @test_intrinsic Core.Intrinsics.fptosi Int Float64(3.3) 3 + @test_intrinsic Core.Intrinsics.fptoui UInt Float64(3.3) UInt(3) +end + +@testset "Float32 intrinsics" begin + # unary + @test_intrinsic Core.Intrinsics.abs_float Float32(-3.3) Float32(3.3) + @test_intrinsic Core.Intrinsics.neg_float Float32(3.3) Float32(-3.3) + @test_intrinsic Core.Intrinsics.fpext Float32 Float32(3.3) Float32(3.3) + @test_intrinsic Core.Intrinsics.fpext Float64 Float32(3.3) 3.299999952316284 + @test_intrinsic Core.Intrinsics.fptrunc Float32 Float64(3.3) Float32(3.3) + + # binary + @test_intrinsic Core.Intrinsics.add_float Float32(3.3) Float32(2) Float32(5.3) + @test_intrinsic Core.Intrinsics.sub_float Float32(3.3) Float32(2) Float32(1.3) + @test_intrinsic Core.Intrinsics.mul_float Float32(3.3) Float32(2) Float32(6.6) + @test_intrinsic Core.Intrinsics.div_float Float32(3.3) Float32(2) Float32(1.65) + @test_intrinsic Core.Intrinsics.max_float Float32(1.0) Float32(2.0) Float32(2.0) + @test_intrinsic Core.Intrinsics.min_float Float32(1.0) Float32(2.0) Float32(1.0) + + # ternary + @test_intrinsic Core.Intrinsics.fma_float Float32(3.3) Float32(4.4) Float32(5.5) Float32(20.02) + @test_intrinsic Core.Intrinsics.muladd_float Float32(3.3) Float32(4.4) Float32(5.5) Float32(20.02) + + # boolean + @test_intrinsic Core.Intrinsics.eq_float Float32(3.3) Float32(3.3) true + @test_intrinsic Core.Intrinsics.eq_float Float32(3.3) Float32(2) false + @test_intrinsic Core.Intrinsics.ne_float Float32(3.3) Float32(3.3) false + @test_intrinsic Core.Intrinsics.ne_float Float32(3.3) Float32(2) true + @test_intrinsic Core.Intrinsics.le_float Float32(3.3) Float32(3.3) true + @test_intrinsic Core.Intrinsics.le_float Float32(3.3) Float32(2) false + + # conversions + @test_intrinsic Core.Intrinsics.sitofp Float32 3 Float32(3.0) + @test_intrinsic Core.Intrinsics.uitofp Float32 UInt(3) Float32(3.0) + @test_intrinsic Core.Intrinsics.fptosi Int Float32(3.3) 3 + @test_intrinsic Core.Intrinsics.fptoui UInt Float32(3.3) UInt(3) +end + @testset "Float16 intrinsics" begin # unary + @test_intrinsic Core.Intrinsics.abs_float Float16(-3.3) Float16(3.3) @test_intrinsic Core.Intrinsics.neg_float Float16(3.3) Float16(-3.3) + # See + #broken @test_intrinsic Core.Intrinsics.fpext Float16 Float16(3.3) Float16(3.3) + @test_broken Core.Intrinsics.fpext(Float16, Float16(3.3)) === Float16(3.3) @test_intrinsic Core.Intrinsics.fpext Float32 Float16(3.3) 3.3007812f0 @test_intrinsic Core.Intrinsics.fpext Float64 Float16(3.3) 3.30078125 @test_intrinsic Core.Intrinsics.fptrunc Float16 Float32(3.3) Float16(3.3) @@ -160,6 +232,8 @@ end @test_intrinsic Core.Intrinsics.sub_float Float16(3.3) Float16(2) Float16(1.301) @test_intrinsic Core.Intrinsics.mul_float Float16(3.3) Float16(2) Float16(6.6) @test_intrinsic Core.Intrinsics.div_float Float16(3.3) Float16(2) Float16(1.65) + @test_intrinsic Core.Intrinsics.max_float Float16(1.0) Float16(2.0) Float16(2.0) + @test_intrinsic Core.Intrinsics.min_float Float16(1.0) Float16(2.0) Float16(1.0) # ternary @test_intrinsic Core.Intrinsics.fma_float Float16(3.3) Float16(4.4) Float16(5.5) Float16(20.02) @@ -174,8 +248,8 @@ end @test_intrinsic Core.Intrinsics.le_float Float16(3.3) Float16(2) false # conversions - @test_intrinsic Core.Intrinsics.sitofp Float16 3 Float16(3f0) - @test_intrinsic Core.Intrinsics.uitofp Float16 UInt(3) Float16(3f0) + @test_intrinsic Core.Intrinsics.sitofp Float16 3 Float16(3.0) + @test_intrinsic Core.Intrinsics.uitofp Float16 UInt(3) Float16(3.0) @test_intrinsic Core.Intrinsics.fptosi Int Float16(3.3) 3 @test_intrinsic Core.Intrinsics.fptoui UInt Float16(3.3) UInt(3) end From f52c4ab7215d81d7fea1fd2f4043b1f20c92dce3 Mon Sep 17 00:00:00 2001 From: Jakob Nybo Nissen Date: Thu, 23 Jan 2025 12:12:15 +0100 Subject: [PATCH 19/56] Expand on docstring of GC.safepoint (#57128) Update the docstring to mention the following points: * Safepoints are very fast, but may still degrade performance in tight loops * Safepoints do not trigger the GC, but instead stops a task from blocking GC that would otherwise run. * Switch terminology from mentioning 'threads' to 'tasks' --- base/gcutils.jl | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/base/gcutils.jl b/base/gcutils.jl index 84a184537ffc0..60b8ecdd17d65 100644 --- a/base/gcutils.jl +++ b/base/gcutils.jl @@ -243,12 +243,21 @@ end GC.safepoint() Inserts a point in the program where garbage collection may run. -This can be useful in rare cases in multi-threaded programs where some threads -are allocating memory (and hence may need to run GC) but other threads are doing -only simple operations (no allocation, task switches, or I/O). -Calling this function periodically in non-allocating threads allows garbage + +Safepoints are fast and do not themselves trigger garbage collection. +However, if another thread has requested the GC to run, reaching a safepoint will +cause the current thread to block and wait for the GC. + +This can be useful in rare cases in multi-threaded programs where some tasks +are allocating memory (and hence may need to run GC) but other tasks are doing +only simple operations (no allocation, task switches, or I/O), which do not +yield control to Julia's runtime, and therefore blocks the GC from running. +Calling this function periodically in the non-allocating tasks allows garbage collection to run. +Note that even though safepoints are fast (typically around 2 clock cycles), +they can still degrade performance if called in a tight loop. + !!! compat "Julia 1.4" This function is available as of Julia 1.4. """ From 88c71dd2164030ef9563a57ef3f1072581d8efca Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Thu, 23 Jan 2025 09:51:06 -0500 Subject: [PATCH 20/56] REPL: Handle message from `complete_methods!` when max methods is hit (#57138) --- stdlib/REPL/src/REPLCompletions.jl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/stdlib/REPL/src/REPLCompletions.jl b/stdlib/REPL/src/REPLCompletions.jl index c63c5a557acd8..26ad6651cd880 100644 --- a/stdlib/REPL/src/REPLCompletions.jl +++ b/stdlib/REPL/src/REPLCompletions.jl @@ -1097,6 +1097,9 @@ function complete_keyword_argument(partial::String, last_idx::Int, context_modul last_word = partial[wordrange] # the word to complete kwargs = Set{String}() for m in methods + # if MAX_METHOD_COMPLETIONS is hit a single TextCompletion is return by complete_methods! with an explanation + # which can be ignored here + m isa TextCompletion && continue m::MethodCompletion possible_kwargs = Base.kwarg_decl(m.method) current_kwarg_candidates = String[] From b76fd9f3148b66d418c1fbac3d729c0d318a654d Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Thu, 23 Jan 2025 15:02:46 -0500 Subject: [PATCH 21/56] codegen: fix unsound mark_volatile_vars implementation (#57131) The previous implementation was incorrect, leading to failing to mark variables correctly. The new implementation is more conservative. This simple analysis assumes that inference has normally run or that performance doesn't matter for a particular block of code. Fixes #56996 --- Compiler/test/codegen.jl | 17 +++++ src/codegen.cpp | 149 ++++++++++++++++++++------------------- 2 files changed, 93 insertions(+), 73 deletions(-) diff --git a/Compiler/test/codegen.jl b/Compiler/test/codegen.jl index ff61ac719c59e..57a9c26aefac6 100644 --- a/Compiler/test/codegen.jl +++ b/Compiler/test/codegen.jl @@ -1041,3 +1041,20 @@ struct Vec56937 x::NTuple{8, VecElement{Int}} end x56937 = Ref(Vec56937(ntuple(_->VecElement(1),8))) @test x56937[].x[1] == VecElement{Int}(1) # shouldn't crash + +# issue #56996 +let + ()->() # trigger various heuristics + Base.Experimental.@force_compile + default_rng_orig = [] # make a value in a Slot + try + # overwrite the gc-slots in the exception branch + throw(ErrorException("This test is supposed to throw an error")) + catch ex + # destroy any values that aren't referenced + GC.gc() + # make sure that default_rng_orig value is still valid + @noinline copy!([], default_rng_orig) + end + nothing +end diff --git a/src/codegen.cpp b/src/codegen.cpp index e047632923f68..60e53f75638ed 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -3282,24 +3282,7 @@ static bool local_var_occurs(jl_value_t *e, int sl) return false; } -static std::set assigned_in_try(jl_array_t *stmts, int s, long l) -{ - std::set av; - for(int i=s; i < l; i++) { - jl_value_t *st = jl_array_ptr_ref(stmts,i); - if (jl_is_expr(st)) { - if (((jl_expr_t*)st)->head == jl_assign_sym) { - jl_value_t *ar = jl_exprarg(st, 0); - if (jl_is_slotnumber(ar)) { - av.insert(jl_slot_number(ar)-1); - } - } - } - } - return av; -} - -static void mark_volatile_vars(jl_array_t *stmts, SmallVectorImpl &slots) +static bool have_try_block(jl_array_t *stmts) { size_t slength = jl_array_dim0(stmts); for (int i = 0; i < (int)slength; i++) { @@ -3308,19 +3291,38 @@ static void mark_volatile_vars(jl_array_t *stmts, SmallVectorImpl int last = jl_enternode_catch_dest(st); if (last == 0) continue; - std::set as = assigned_in_try(stmts, i + 1, last - 1); - for (int j = 0; j < (int)slength; j++) { - if (j < i || j > last) { - std::set::iterator it = as.begin(); - for (; it != as.end(); it++) { - if (local_var_occurs(jl_array_ptr_ref(stmts, j), *it)) { - jl_varinfo_t &vi = slots[*it]; - vi.isVolatile = true; - } - } + return 1; + } + } + return 0; +} + +// conservative marking of all variables potentially used after a catch block that were assigned before it +static void mark_volatile_vars(jl_array_t *stmts, SmallVectorImpl &slots, const std::set &bbstarts) +{ + if (!have_try_block(stmts)) + return; + size_t slength = jl_array_dim0(stmts); + BitVector assigned_in_block(slots.size()); // conservatively only ignore slots assigned in the same basic block + for (int j = 0; j < (int)slength; j++) { + if (bbstarts.count(j + 1)) + assigned_in_block.reset(); + jl_value_t *stmt = jl_array_ptr_ref(stmts, j); + if (jl_is_expr(stmt)) { + jl_expr_t *e = (jl_expr_t*)stmt; + if (e->head == jl_assign_sym) { + jl_value_t *l = jl_exprarg(e, 0); + if (jl_is_slotnumber(l)) { + assigned_in_block.set(jl_slot_number(l)-1); } } } + for (int slot = 0; slot < (int)slots.size(); slot++) { + if (!assigned_in_block.test(slot) && local_var_occurs(stmt, slot)) { + jl_varinfo_t &vi = slots[slot]; + vi.isVolatile = true; + } + } } } @@ -8439,7 +8441,6 @@ static jl_llvm_functions_t ctx.code = src->code; ctx.source = src; - std::map labels; ctx.module = jl_is_method(lam->def.method) ? lam->def.method->module : lam->def.module; ctx.linfo = lam; ctx.name = name_from_method_instance(lam); @@ -8499,6 +8500,49 @@ static jl_llvm_functions_t if (dbgFuncName.empty()) // Should never happen anymore? debug_enabled = false; + // First go through and collect all branch targets, so we know where to + // split basic blocks. + std::set branch_targets; // 1-indexed, sorted + for (size_t i = 0; i < stmtslen; ++i) { + jl_value_t *stmt = jl_array_ptr_ref(stmts, i); + if (jl_is_gotoifnot(stmt)) { + int dest = jl_gotoifnot_label(stmt); + branch_targets.insert(dest); + // The next 1-indexed statement + branch_targets.insert(i + 2); + } + else if (jl_is_returnnode(stmt)) { + // We don't do dead branch elimination before codegen + // so we need to make sure to start a BB after any + // return node, even if they aren't otherwise branch + // targets. + if (i + 2 <= stmtslen) + branch_targets.insert(i + 2); + } + else if (jl_is_enternode(stmt)) { + branch_targets.insert(i + 1); + if (i + 2 <= stmtslen) + branch_targets.insert(i + 2); + size_t catch_dest = jl_enternode_catch_dest(stmt); + if (catch_dest) + branch_targets.insert(catch_dest); + } + else if (jl_is_gotonode(stmt)) { + int dest = jl_gotonode_label(stmt); + branch_targets.insert(dest); + if (i + 2 <= stmtslen) + branch_targets.insert(i + 2); + } + else if (jl_is_phinode(stmt)) { + jl_array_t *edges = (jl_array_t*)jl_fieldref_noalloc(stmt, 0); + for (size_t j = 0; j < jl_array_nrows(edges); ++j) { + size_t edge = jl_array_data(edges, int32_t)[j]; + if (edge == i) + branch_targets.insert(i + 1); + } + } + } + // step 2. process var-info lists to see what vars need boxing int n_ssavalues = jl_is_long(src->ssavaluetypes) ? jl_unbox_long(src->ssavaluetypes) : jl_array_nrows(src->ssavaluetypes); size_t vinfoslen = jl_array_dim0(src->slotflags); @@ -8559,7 +8603,7 @@ static jl_llvm_functions_t simple_use_analysis(ctx, jl_array_ptr_ref(stmts, i)); // determine which vars need to be volatile - mark_volatile_vars(stmts, ctx.slots); + mark_volatile_vars(stmts, ctx.slots, branch_targets); // step 4. determine function signature if (!specsig) @@ -9310,8 +9354,8 @@ static jl_llvm_functions_t // step 11b. Do codegen in control flow order SmallVector workstack; - std::map BB; - std::map come_from_bb; + DenseMap BB; + DenseMap come_from_bb; int cursor = 0; int current_label = 0; auto find_next_stmt = [&] (int seq_next) { @@ -9430,47 +9474,6 @@ static jl_llvm_functions_t come_from_bb[0] = ctx.builder.GetInsertBlock(); - // First go through and collect all branch targets, so we know where to - // split basic blocks. - std::set branch_targets; // 1-indexed - { - for (size_t i = 0; i < stmtslen; ++i) { - jl_value_t *stmt = jl_array_ptr_ref(stmts, i); - if (jl_is_gotoifnot(stmt)) { - int dest = jl_gotoifnot_label(stmt); - branch_targets.insert(dest); - // The next 1-indexed statement - branch_targets.insert(i + 2); - } else if (jl_is_returnnode(stmt)) { - // We don't do dead branch elimination before codegen - // so we need to make sure to start a BB after any - // return node, even if they aren't otherwise branch - // targets. - if (i + 2 <= stmtslen) - branch_targets.insert(i + 2); - } else if (jl_is_enternode(stmt)) { - branch_targets.insert(i + 1); - if (i + 2 <= stmtslen) - branch_targets.insert(i + 2); - size_t catch_dest = jl_enternode_catch_dest(stmt); - if (catch_dest) - branch_targets.insert(catch_dest); - } else if (jl_is_gotonode(stmt)) { - int dest = jl_gotonode_label(stmt); - branch_targets.insert(dest); - if (i + 2 <= stmtslen) - branch_targets.insert(i + 2); - } else if (jl_is_phinode(stmt)) { - jl_array_t *edges = (jl_array_t*)jl_fieldref_noalloc(stmt, 0); - for (size_t j = 0; j < jl_array_nrows(edges); ++j) { - size_t edge = jl_array_data(edges, int32_t)[j]; - if (edge == i) - branch_targets.insert(i + 1); - } - } - } - } - for (int label : branch_targets) { BasicBlock *bb = BasicBlock::Create(ctx.builder.getContext(), "L" + std::to_string(label), f); From 3054c681785473d689290da96205956a962fa283 Mon Sep 17 00:00:00 2001 From: Gabriel Baraldi Date: Thu, 23 Jan 2025 20:36:33 -0300 Subject: [PATCH 22/56] Don't use altstack for the sigusr2 handler (#57134) This works around an rr bug with nested signal handlers and syscalls. But it's also mostly unecessary as it doesn't need to handle stack overflows --- src/signals-unix.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/signals-unix.c b/src/signals-unix.c index 788539b1f5096..c730f27f16def 100644 --- a/src/signals-unix.c +++ b/src/signals-unix.c @@ -1208,7 +1208,7 @@ void jl_install_default_signal_handlers(void) memset(&act, 0, sizeof(struct sigaction)); sigemptyset(&act.sa_mask); act.sa_sigaction = usr2_handler; - act.sa_flags = SA_ONSTACK | SA_SIGINFO | SA_RESTART; + act.sa_flags = SA_SIGINFO | SA_RESTART; if (sigaction(SIGUSR2, &act, NULL) < 0) { jl_errorf("fatal error: sigaction: %s", strerror(errno)); } From 6eb42dbc69140e3bf4583a2b87b50bf3e7bdd29f Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Fri, 24 Jan 2025 02:23:29 +0100 Subject: [PATCH 23/56] Fix missing nargs setting in abioverride test (#57144) The only place that really reads this is the debug info generation, which is not enabled by default, but this test would crash if you ran it on a debug build of julia. --- Compiler/test/abioverride.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/Compiler/test/abioverride.jl b/Compiler/test/abioverride.jl index da9b1f92786e5..49907ea8e4c63 100644 --- a/Compiler/test/abioverride.jl +++ b/Compiler/test/abioverride.jl @@ -40,6 +40,7 @@ let world = Base.tls_world_age() ## Remove the argument resize!(new_source.slotnames, 2) resize!(new_source.slotflags, 2) + new_source.nargs = 2 # Construct the CodeInstance from the modified CodeInfo data global new_ci = Core.CodeInstance(Core.ABIOverride(Tuple{typeof(myplus), Int}, mi), From bcddc653386f09b6f6aa7ebe5d1151ee1d4a6c82 Mon Sep 17 00:00:00 2001 From: DilumAluthgeBot <43731525+DilumAluthgeBot@users.noreply.github.com> Date: Fri, 24 Jan 2025 14:25:58 -0500 Subject: [PATCH 24/56] =?UTF-8?q?=F0=9F=A4=96=20[master]=20Bump=20the=20Li?= =?UTF-8?q?nearAlgebra=20stdlib=20from=201137b4c=20to=20da6d052=20(#57151)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Stdlib: LinearAlgebra URL: https://github.com/JuliaLang/LinearAlgebra.jl.git Stdlib branch: master Julia branch: master Old commit: 1137b4c New commit: da6d052 Julia version: 1.12.0-DEV LinearAlgebra version: 1.12.0 Bump invoked by: @dkarrasch Powered by: [BumpStdlibs.jl](https://github.com/JuliaLang/BumpStdlibs.jl) Diff: https://github.com/JuliaLang/LinearAlgebra.jl/compare/1137b4c7fa8297cef17c4ae0982d7d89d4ab7dd8...da6d0521347daf5e42b3d09cdb757d4488528c7b ``` $ git log --oneline 1137b4c..da6d052 da6d052 Bump version number to v1.12.0 (#1180) cecf025 test: use `BLAS.libblastrampoline` (#1170) adcd1ea Delete .ci/Manifest.toml (#1173) 53032fe Rearrange tests (#1157) beee169 Delete .ci/Manifest.toml c869685 test: use `BLAS.libblastrampoline` 9bc292d Improve AbstractQ coverage (#1160) a622302 Consistently check matrix sizes in matmul (#1152) 6e5ea12 `sqrt`, `cbrt` and `log` for dense diagonal matrices (#1156) 959d985 Remove old debug functionality in triangular.jl (#1124) 8d9b14f Higher-order functions in `factorize` to obtain structure (#1135) 7c0ecd6 avoid division by zero in svd(::Diagonal) (#1150) 39a7e3c docs: fix unexported function names in svd doc (#1151) ``` Co-authored-by: dkarrasch <26658441+dkarrasch@users.noreply.github.com> --- .../md5 | 1 - .../sha512 | 1 - .../md5 | 1 + .../sha512 | 1 + stdlib/LinearAlgebra.version | 2 +- 5 files changed, 3 insertions(+), 3 deletions(-) delete mode 100644 deps/checksums/LinearAlgebra-1137b4c7fa8297cef17c4ae0982d7d89d4ab7dd8.tar.gz/md5 delete mode 100644 deps/checksums/LinearAlgebra-1137b4c7fa8297cef17c4ae0982d7d89d4ab7dd8.tar.gz/sha512 create mode 100644 deps/checksums/LinearAlgebra-da6d0521347daf5e42b3d09cdb757d4488528c7b.tar.gz/md5 create mode 100644 deps/checksums/LinearAlgebra-da6d0521347daf5e42b3d09cdb757d4488528c7b.tar.gz/sha512 diff --git a/deps/checksums/LinearAlgebra-1137b4c7fa8297cef17c4ae0982d7d89d4ab7dd8.tar.gz/md5 b/deps/checksums/LinearAlgebra-1137b4c7fa8297cef17c4ae0982d7d89d4ab7dd8.tar.gz/md5 deleted file mode 100644 index 5bd44506fd874..0000000000000 --- a/deps/checksums/LinearAlgebra-1137b4c7fa8297cef17c4ae0982d7d89d4ab7dd8.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -eb4df255412ad9a05b807010f626afc8 diff --git a/deps/checksums/LinearAlgebra-1137b4c7fa8297cef17c4ae0982d7d89d4ab7dd8.tar.gz/sha512 b/deps/checksums/LinearAlgebra-1137b4c7fa8297cef17c4ae0982d7d89d4ab7dd8.tar.gz/sha512 deleted file mode 100644 index 23617698dd26e..0000000000000 --- a/deps/checksums/LinearAlgebra-1137b4c7fa8297cef17c4ae0982d7d89d4ab7dd8.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -3b4bf7b761d9585fb2d5c5b8418770be4d1d4399a5f25dd5b2e08785506f0732c8e140ada6f82f6d8a7a77a2c2f79e2feecd6eb0e19eda0c3ee519ba554c19ec diff --git a/deps/checksums/LinearAlgebra-da6d0521347daf5e42b3d09cdb757d4488528c7b.tar.gz/md5 b/deps/checksums/LinearAlgebra-da6d0521347daf5e42b3d09cdb757d4488528c7b.tar.gz/md5 new file mode 100644 index 0000000000000..1771370455266 --- /dev/null +++ b/deps/checksums/LinearAlgebra-da6d0521347daf5e42b3d09cdb757d4488528c7b.tar.gz/md5 @@ -0,0 +1 @@ +810a9f842ee40d03e55c698130620963 diff --git a/deps/checksums/LinearAlgebra-da6d0521347daf5e42b3d09cdb757d4488528c7b.tar.gz/sha512 b/deps/checksums/LinearAlgebra-da6d0521347daf5e42b3d09cdb757d4488528c7b.tar.gz/sha512 new file mode 100644 index 0000000000000..f696212eda4fc --- /dev/null +++ b/deps/checksums/LinearAlgebra-da6d0521347daf5e42b3d09cdb757d4488528c7b.tar.gz/sha512 @@ -0,0 +1 @@ +4197a863c0f5052f0b85f1c04b6f42f71bf35af572f507dbc475e868a7c057bebd11f8258451b7d0a728012f238abfd88943fe80b83df63728af2fcc01199604 diff --git a/stdlib/LinearAlgebra.version b/stdlib/LinearAlgebra.version index 3dac27119172a..e877475771829 100644 --- a/stdlib/LinearAlgebra.version +++ b/stdlib/LinearAlgebra.version @@ -1,4 +1,4 @@ LINEARALGEBRA_BRANCH = master -LINEARALGEBRA_SHA1 = 1137b4c7fa8297cef17c4ae0982d7d89d4ab7dd8 +LINEARALGEBRA_SHA1 = da6d0521347daf5e42b3d09cdb757d4488528c7b LINEARALGEBRA_GIT_URL := https://github.com/JuliaLang/LinearAlgebra.jl.git LINEARALGEBRA_TAR_URL = https://api.github.com/repos/JuliaLang/LinearAlgebra.jl/tarball/$1 From 899d2f50ab9a8b9d29365e093f480874d64bdd31 Mon Sep 17 00:00:00 2001 From: Eduardo Souza Date: Sat, 25 Jan 2025 06:37:50 +1100 Subject: [PATCH 25/56] Skip some stock GC-specific tests when using MMTk (#57104) As noted in https://github.com/JuliaLang/julia/issues/57103, there are a few tests that are specific to the stock GC and will fail for MMTk. For now, this PR simply skips those tests when not using the stock GC until we agree on how to approach the incompatibilities described in the issue. --- base/Base.jl | 2 ++ base/gcutils.jl | 11 ++++++++ base/timing.jl | 6 ++-- stdlib/Profile/test/allocs.jl | 13 +++++++-- stdlib/Profile/test/runtests.jl | 3 ++ test/checked.jl | 8 ++++-- test/cmdlineargs.jl | 49 +++++++++++++++++++-------------- test/gc.jl | 12 +++++--- test/misc.jl | 4 ++- 9 files changed, 75 insertions(+), 33 deletions(-) diff --git a/base/Base.jl b/base/Base.jl index 04f732a4309c9..379347f027f69 100644 --- a/base/Base.jl +++ b/base/Base.jl @@ -128,6 +128,8 @@ include("sysinfo.jl") include("libc.jl") using .Libc: getpid, gethostname, time, memcpy, memset, memmove, memcmp +const USING_STOCK_GC = occursin("stock", GC.gc_active_impl()) + # These used to be in build_h.jl and are retained for backwards compatibility. # NOTE: keep in sync with `libblastrampoline_jll.libblastrampoline`. const libblas_name = "libblastrampoline" * (Sys.iswindows() ? "-5" : "") diff --git a/base/gcutils.jl b/base/gcutils.jl index 60b8ecdd17d65..d5e6f4597739f 100644 --- a/base/gcutils.jl +++ b/base/gcutils.jl @@ -281,4 +281,15 @@ function logging_enabled() ccall(:jl_is_gc_logging_enabled, Cint, ()) != 0 end +""" + GC.gc_active_impl() + +Return a string stating which GC implementation is being used and possibly +its version according to the list of supported GCs +""" +function gc_active_impl() + unsafe_string(ccall(:jl_gc_active_impl, Ptr{UInt8}, ())) +end + + end # module GC diff --git a/base/timing.jl b/base/timing.jl index 65c2a643d6a52..61fa73f2eff62 100644 --- a/base/timing.jl +++ b/base/timing.jl @@ -109,10 +109,8 @@ function gc_page_utilization_data() return Base.unsafe_wrap(Array, page_utilization_raw, JL_GC_N_MAX_POOLS, own=false) end - -const USING_STOCK_GC = occursin("stock", unsafe_string(ccall(:jl_gc_active_impl, Ptr{UInt8}, ()))) # Full sweep reasons are currently only available for the stock GC -@static if USING_STOCK_GC +@static if Base.USING_STOCK_GC # must be kept in sync with `src/gc-stock.h`` const FULL_SWEEP_REASONS = [:FULL_SWEEP_REASON_SWEEP_ALWAYS_FULL, :FULL_SWEEP_REASON_FORCED_FULL_SWEEP, :FULL_SWEEP_REASON_USER_MAX_EXCEEDED, :FULL_SWEEP_REASON_LARGE_PROMOTION_RATE] @@ -135,7 +133,7 @@ function full_sweep_reasons() d = Dict{Symbol, Int64}() # populate the dictionary according to the reasons above for the stock GC # otherwise return an empty dictionary for now - @static if USING_STOCK_GC + @static if Base.USING_STOCK_GC reason = cglobal(:jl_full_sweep_reasons, UInt64) reasons_as_array = Base.unsafe_wrap(Vector{UInt64}, reason, length(FULL_SWEEP_REASONS), own=false) for (i, r) in enumerate(FULL_SWEEP_REASONS) diff --git a/stdlib/Profile/test/allocs.jl b/stdlib/Profile/test/allocs.jl index d4930a2b7f5ed..5607783c782f9 100644 --- a/stdlib/Profile/test/allocs.jl +++ b/stdlib/Profile/test/allocs.jl @@ -73,8 +73,14 @@ end @test length(first_alloc.stacktrace) > 0 @test length(string(first_alloc.type)) > 0 - @testset for type in (Task, Vector{Float64},) - @test length(filter(a->a.type <: type, profile.allocs)) >= NUM_TASKS + # Issue #57103: This test does not work with MMTk because of fastpath + # allocation which never calls the allocation profiler. + # TODO: We should port these observability tools (e.g. allocation + # profiler and heap snapshot) to MMTk + @static if Base.USING_STOCK_GC + @testset for type in (Task, Vector{Float64},) + @test length(filter(a->a.type <: type, profile.allocs)) >= NUM_TASKS + end end # TODO: it would be nice to assert that these tasks @@ -143,6 +149,8 @@ end @test length([a for a in prof.allocs if a.type == String]) >= 1 end +# FIXME: Issue #57103 disabling test for MMTk. +@static if Base.USING_STOCK_GC @testset "alloc profiler catches allocs from codegen" begin @eval begin struct MyType x::Int; y::Int end @@ -162,6 +170,7 @@ end @test length(prof.allocs) >= 1 @test length([a for a in prof.allocs if a.type == MyType]) >= 1 end +end @testset "alloc profiler catches allocs from buffer resize" begin f(a) = for _ in 1:100; push!(a, 1); end diff --git a/stdlib/Profile/test/runtests.jl b/stdlib/Profile/test/runtests.jl index b73a2a618011b..e7877b949a17e 100644 --- a/stdlib/Profile/test/runtests.jl +++ b/stdlib/Profile/test/runtests.jl @@ -344,6 +344,8 @@ end @test only(node.down).first == lidict[8] end +# FIXME: Issue #57103: heap snapshots are currently not supported in MMTk +@static if Base.USING_STOCK_GC @testset "HeapSnapshot" begin tmpdir = mktempdir() @@ -374,6 +376,7 @@ end rm(fname) rm(tmpdir, force = true, recursive = true) end +end @testset "PageProfile" begin fname = "$(getpid())_$(time_ns())" diff --git a/test/checked.jl b/test/checked.jl index 4031918a38730..b93c8796162c5 100644 --- a/test/checked.jl +++ b/test/checked.jl @@ -331,8 +331,12 @@ end @test checked_pow(BigInt(2), 2) == BigInt(4) @test checked_pow(BigInt(2), 100) == BigInt(1267650600228229401496703205376) - # Perf test: Make sure BigInts allocs don't scale with the power: - @test @allocations(checked_pow(BigInt(2), 2)) ≈ @allocations(checked_pow(BigInt(2), 10000)) rtol=0.9 + # FIXME: Issue #57103: the following test may fail because + # allocation may not be logged via MMTk's fastpath allocation + @static if Base.USING_STOCK_GC + # Perf test: Make sure BigInts allocs don't scale with the power: + @test @allocations(checked_pow(BigInt(2), 2)) ≈ @allocations(checked_pow(BigInt(2), 10000)) rtol=0.9 + end end @testset "Additional tests" begin diff --git a/test/cmdlineargs.jl b/test/cmdlineargs.jl index 3ff7836223b84..ec7a51e804d3f 100644 --- a/test/cmdlineargs.jl +++ b/test/cmdlineargs.jl @@ -383,29 +383,33 @@ let exename = `$(Base.julia_cmd()) --startup-file=no --color=no` @test p.exitcode == 1 && p.termsignal == 0 end - # --gcthreads - code = "print(Threads.ngcthreads())" - cpu_threads = ccall(:jl_effective_threads, Int32, ()) - @test string(cpu_threads) == - read(`$exename --threads auto -e $code`, String) == - read(`$exename --threads=auto -e $code`, String) == - read(`$exename -tauto -e $code`, String) == - read(`$exename -t auto -e $code`, String) - for nt in (nothing, "1") - withenv("JULIA_NUM_GC_THREADS" => nt) do - @test read(`$exename --gcthreads=2 -e $code`, String) == "2" - end - withenv("JULIA_NUM_GC_THREADS" => nt) do - @test read(`$exename --gcthreads=2,1 -e $code`, String) == "3" + # FIXME: Issue #57103 --gcthreads does not have the same semantics + # for Stock GC and MMTk, so the tests below are specific to the Stock GC + @static if Base.USING_STOCK_GC + # --gcthreads + code = "print(Threads.ngcthreads())" + cpu_threads = ccall(:jl_effective_threads, Int32, ()) + @test string(cpu_threads) == + read(`$exename --threads auto -e $code`, String) == + read(`$exename --threads=auto -e $code`, String) == + read(`$exename -tauto -e $code`, String) == + read(`$exename -t auto -e $code`, String) + for nt in (nothing, "1") + withenv("JULIA_NUM_GC_THREADS" => nt) do + @test read(`$exename --gcthreads=2 -e $code`, String) == "2" + end + withenv("JULIA_NUM_GC_THREADS" => nt) do + @test read(`$exename --gcthreads=2,1 -e $code`, String) == "3" + end end - end - withenv("JULIA_NUM_GC_THREADS" => 2) do - @test read(`$exename -e $code`, String) == "2" - end + withenv("JULIA_NUM_GC_THREADS" => 2) do + @test read(`$exename -e $code`, String) == "2" + end - withenv("JULIA_NUM_GC_THREADS" => "2,1") do - @test read(`$exename -e $code`, String) == "3" + withenv("JULIA_NUM_GC_THREADS" => "2,1") do + @test read(`$exename -e $code`, String) == "3" + end end # --machine-file @@ -1182,6 +1186,10 @@ end end end +# FIXME: Issue #57103: MMTK currently does not use --heap-size-hint since it only +# supports setting up a hard limit unlike the Stock GC +# which takes it as a soft limit. For now, we skip the tests below for MMTk +@static if Base.USING_STOCK_GC @testset "heap size hint" begin #heap-size-hint, we reserve 250 MB for non GC memory (llvm, etc.) @test readchomp(`$(Base.julia_cmd()) --startup-file=no --heap-size-hint=500M -e "println(@ccall jl_gc_get_max_memory()::UInt64)"`) == "$((500-250)*1024*1024)" @@ -1201,6 +1209,7 @@ end @test readchomp(`$(Base.julia_cmd()) --startup-file=no --heap-size-hint=10M -e "println(@ccall jl_gc_get_max_memory()::UInt64)"`) == "$(1*1024*1024)" end +end ## `Main.main` entrypoint diff --git a/test/gc.jl b/test/gc.jl index c532f17f04eb5..3e9f03ef40d92 100644 --- a/test/gc.jl +++ b/test/gc.jl @@ -67,6 +67,9 @@ end run_gctest("gc/chunks.jl") end +#FIXME: Issue #57103 disabling tests for MMTk, since +# they rely on information that is specific to the stock GC. +@static if Base.USING_STOCK_GC @testset "GC page metrics" begin run_nonzero_page_utilization_test() run_pg_size_test() @@ -76,13 +79,14 @@ end issue_54275_test() end -@testset "Base.GC docstrings" begin - @test isempty(Docs.undocumented_names(GC)) -end - @testset "Full GC reasons" begin full_sweep_reasons_test() end +end + +@testset "Base.GC docstrings" begin + @test isempty(Docs.undocumented_names(GC)) +end #testset doesn't work here because this needs to run in top level #Check that we ensure objects in toplevel exprs are rooted diff --git a/test/misc.jl b/test/misc.jl index 7070fd49f5f36..070952db89032 100644 --- a/test/misc.jl +++ b/test/misc.jl @@ -1453,7 +1453,8 @@ end @test_throws ErrorException finalizer(x->nothing, 1) @test_throws ErrorException finalizer(C_NULL, 1) - +# FIXME: Issue #57103 Test is specific to Stock GC +@static if Base.USING_STOCK_GC @testset "GC utilities" begin GC.gc() GC.gc(true); GC.gc(false) @@ -1473,6 +1474,7 @@ end @test occursin("GC: pause", read(tmppath, String)) end end +end @testset "fieldtypes Module" begin @test fieldtypes(Module) === () From 246e408757170fb2b600454a853a80777934c88e Mon Sep 17 00:00:00 2001 From: Sam Schweigel <33556084+xal-0@users.noreply.github.com> Date: Fri, 24 Jan 2025 16:28:53 -0800 Subject: [PATCH 26/56] Fix JULIA_EXCLUSIVE setting affinity on non-worker threads (#57136) With JULIA_EXCLUSIVE=1, we would try to give both worker and interactive threads an exclusive CPU, causing --threads=auto to produce a "Too many threads requested" error. Fixes #50702. --- doc/src/manual/environment-variables.md | 7 +++-- src/init.c | 4 +-- src/julia.h | 3 ++ src/threading.c | 42 +++++++++++++++---------- test/threads.jl | 6 ++-- 5 files changed, 40 insertions(+), 22 deletions(-) diff --git a/doc/src/manual/environment-variables.md b/doc/src/manual/environment-variables.md index b3bfa5204e603..ff505db1f11f2 100644 --- a/doc/src/manual/environment-variables.md +++ b/doc/src/manual/environment-variables.md @@ -397,8 +397,11 @@ during image compilation. Defaults to 0. ### [`JULIA_EXCLUSIVE`](@id JULIA_EXCLUSIVE) If set to anything besides `0`, then Julia's thread policy is consistent with -running on a dedicated machine: the master thread is on proc 0, and threads are -affinitized. Otherwise, Julia lets the operating system handle thread policy. +running on a dedicated machine: each thread in the default threadpool is +affinitized. [Interactive threads](@ref man-threadpools) remain under the +control of the operating system scheduler. + +Otherwise, Julia lets the operating system handle thread policy. ## Garbage Collection diff --git a/src/init.c b/src/init.c index e69467c75bd73..f383a8f4620be 100644 --- a/src/init.c +++ b/src/init.c @@ -907,8 +907,8 @@ static NOINLINE void _finish_julia_init(JL_IMAGE_SEARCH rel, jl_ptls_t ptls, jl_ jl_n_markthreads = 0; jl_n_sweepthreads = 0; jl_n_gcthreads = 0; - jl_n_threads_per_pool[0] = 0; // Interactive threadpool - jl_n_threads_per_pool[1] = 1; // Default threadpool + jl_n_threads_per_pool[JL_THREADPOOL_ID_INTERACTIVE] = 0; + jl_n_threads_per_pool[JL_THREADPOOL_ID_DEFAULT] = 1; } else { post_image_load_hooks(); } diff --git a/src/julia.h b/src/julia.h index 4c699ba059c65..1088f669ad773 100644 --- a/src/julia.h +++ b/src/julia.h @@ -2053,6 +2053,9 @@ extern JL_DLLIMPORT _Atomic(int) jl_n_threads; extern JL_DLLIMPORT int jl_n_gcthreads; extern int jl_n_markthreads; extern int jl_n_sweepthreads; + +#define JL_THREADPOOL_ID_INTERACTIVE 0 +#define JL_THREADPOOL_ID_DEFAULT 1 extern JL_DLLIMPORT int *jl_n_threads_per_pool; // environment entries diff --git a/src/threading.c b/src/threading.c index 77956786af3f4..ab63114fc9c8f 100644 --- a/src/threading.c +++ b/src/threading.c @@ -778,9 +778,9 @@ void jl_init_threading(void) } jl_all_tls_states_size = nthreads + nthreadsi + ngcthreads; - jl_n_threads_per_pool = (int*)malloc_s(2 * sizeof(int)); - jl_n_threads_per_pool[0] = nthreadsi; - jl_n_threads_per_pool[1] = nthreads; + jl_n_threads_per_pool = (int*)calloc_s(jl_n_threadpools * sizeof(int)); + jl_n_threads_per_pool[JL_THREADPOOL_ID_INTERACTIVE] = nthreadsi; + jl_n_threads_per_pool[JL_THREADPOOL_ID_DEFAULT] = nthreads; assert(jl_all_tls_states_size > 0); jl_atomic_store_release(&jl_all_tls_states, (jl_ptls_t*)calloc(jl_all_tls_states_size, sizeof(jl_ptls_t))); jl_atomic_store_release(&jl_n_threads, jl_all_tls_states_size); @@ -793,7 +793,10 @@ uv_barrier_t thread_init_done; void jl_start_threads(void) { int nthreads = jl_atomic_load_relaxed(&jl_n_threads); - int ngcthreads = jl_n_gcthreads; + int ninteractive_threads = jl_n_threads_per_pool[JL_THREADPOOL_ID_INTERACTIVE]; + int ndefault_threads = jl_n_threads_per_pool[JL_THREADPOOL_ID_DEFAULT]; + int nmutator_threads = nthreads - jl_n_gcthreads; + int cpumasksize = uv_cpumask_size(); char *cp; int i, exclusive; @@ -808,36 +811,43 @@ void jl_start_threads(void) if (cp && strcmp(cp, "0") != 0) exclusive = 1; - // exclusive use: affinitize threads, master thread on proc 0, rest - // according to a 'compact' policy + // exclusive use: affinitize threads, master thread on proc 0, threads in + // default pool according to a 'compact' policy // non-exclusive: no affinity settings; let the kernel move threads about if (exclusive) { - if (nthreads > jl_cpu_threads()) { + if (ndefault_threads > jl_cpu_threads()) { jl_printf(JL_STDERR, "ERROR: Too many threads requested for %s option.\n", MACHINE_EXCLUSIVE_NAME); exit(1); } memset(mask, 0, cpumasksize); - mask[0] = 1; - uvtid = uv_thread_self(); - uv_thread_setaffinity(&uvtid, mask, NULL, cpumasksize); - mask[0] = 0; + + // If there are no interactive threads, the master thread is in the + // default pool and we must affinitize it + if (ninteractive_threads == 0) { + mask[0] = 1; + uvtid = uv_thread_self(); + uv_thread_setaffinity(&uvtid, mask, NULL, cpumasksize); + mask[0] = 0; + } } // create threads uv_barrier_init(&thread_init_done, nthreads); // GC/System threads need to be after the worker threads. - int nmutator_threads = nthreads - ngcthreads; - for (i = 1; i < nmutator_threads; ++i) { jl_threadarg_t *t = (jl_threadarg_t *)malloc_s(sizeof(jl_threadarg_t)); // ownership will be passed to the thread t->tid = i; t->barrier = &thread_init_done; uv_thread_create(&uvtid, jl_threadfun, t); - if (exclusive) { - mask[i] = 1; + + // Interactive pool threads get the low IDs, so check if this is a + // default pool thread. The master thread is already on CPU 0. + if (exclusive && i >= ninteractive_threads) { + assert(i - ninteractive_threads < cpumasksize); + mask[i - ninteractive_threads] = 1; uv_thread_setaffinity(&uvtid, mask, NULL, cpumasksize); - mask[i] = 0; + mask[i - ninteractive_threads] = 0; } uv_thread_detach(&uvtid); } diff --git a/test/threads.jl b/test/threads.jl index 179279dbab4e6..43fbea1e351a2 100644 --- a/test/threads.jl +++ b/test/threads.jl @@ -123,10 +123,11 @@ if AFFINITY_SUPPORTED end end -function get_nthreads(options = ``; cpus = nothing) +function get_nthreads(options = ``; cpus = nothing, exclusive = false) cmd = `$(Base.julia_cmd()) --startup-file=no $(options)` cmd = `$cmd -e "print(Threads.threadpoolsize())"` - cmd = addenv(cmd, "JULIA_EXCLUSIVE" => "0", "JULIA_NUM_THREADS" => "auto") + cmd = addenv(cmd, "JULIA_EXCLUSIVE" => exclusive ? "1" : "0", + "JULIA_NUM_THREADS" => "auto") if cpus !== nothing cmd = setcpuaffinity(cmd, cpus) end @@ -138,6 +139,7 @@ end allowed_cpus = findall(uv_thread_getaffinity()) if length(allowed_cpus) ≥ 2 @test get_nthreads() ≥ 2 + @test get_nthreads(exclusive = true) ≥ 2 @test get_nthreads(cpus = allowed_cpus[1:1]) == 1 @test get_nthreads(cpus = allowed_cpus[2:2]) == 1 @test get_nthreads(cpus = allowed_cpus[1:2]) == 2 From 6cd750ddf75a2069e6bea8c84b243e76edfcf815 Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Sat, 25 Jan 2025 03:50:41 +0100 Subject: [PATCH 27/56] bpart: Give a warning when accessing a backdated const binding (#57133) This implements the strategy proposed in https://github.com/JuliaLang/julia/pull/57102#issuecomment-2605511266. Example: ``` julia> function foo(i) eval(:(const x = $i)) x end foo (generic function with 1 method) julia> foo(1) WARNING: Detected access to binding Main.x in a world prior to its definition world. Julia 1.12 has introduced more strict world age semantics for global bindings. !!! This code may malfunction under Revise. !!! This code will error in future versions of Julia. Hint: Add an appropriate `invokelatest` around the access to this binding. 1 ``` The warning is triggered once per binding to avoid spamming for repeated access. --- Compiler/src/Compiler.jl | 3 +- Compiler/src/abstractinterpretation.jl | 5 ++ Compiler/src/ssair/slot2ssa.jl | 4 -- Compiler/src/ssair/verify.jl | 11 +++- Compiler/test/invalidation.jl | 2 +- base/Base_compiler.jl | 4 ++ base/boot.jl | 8 ++- base/docs/bindings.jl | 4 +- base/runtime_internals.jl | 3 +- base/show.jl | 17 +++--- doc/src/manual/methods.md | 5 +- src/codegen.cpp | 3 +- src/julia.h | 22 +++++--- src/julia_internal.h | 12 +++- src/module.c | 77 +++++++++++++++++++++----- src/rtutils.c | 6 +- src/toplevel.c | 23 +++++--- stdlib/REPL/src/REPL.jl | 4 +- stdlib/Test/src/Test.jl | 21 ++++--- test/worlds.jl | 8 +-- 20 files changed, 171 insertions(+), 71 deletions(-) diff --git a/Compiler/src/Compiler.jl b/Compiler/src/Compiler.jl index ea939f86422c5..b83dc0b5970e1 100644 --- a/Compiler/src/Compiler.jl +++ b/Compiler/src/Compiler.jl @@ -49,7 +49,8 @@ using Core: ABIOverride, Builtin, CodeInstance, IntrinsicFunction, MethodInstanc using Base using Base: @_foldable_meta, @_gc_preserve_begin, @_gc_preserve_end, @nospecializeinfer, - BINDING_KIND_GLOBAL, BINDING_KIND_UNDEF_CONST, Base, BitVector, Bottom, Callable, DataTypeFieldDesc, + BINDING_KIND_GLOBAL, BINDING_KIND_UNDEF_CONST, BINDING_KIND_BACKDATED_CONST, + Base, BitVector, Bottom, Callable, DataTypeFieldDesc, EffectsOverride, Filter, Generator, IteratorSize, JLOptions, NUM_EFFECTS_OVERRIDES, OneTo, Ordering, RefValue, SizeUnknown, _NAMEDTUPLE_NAME, _array_for, _bits_findnext, _methods_by_ftype, _uniontypes, all, allocatedinline, any, diff --git a/Compiler/src/abstractinterpretation.jl b/Compiler/src/abstractinterpretation.jl index 2b1a7fb2dd448..dbd7e76615116 100644 --- a/Compiler/src/abstractinterpretation.jl +++ b/Compiler/src/abstractinterpretation.jl @@ -3524,6 +3524,11 @@ function abstract_eval_partition_load(interp::AbstractInterpreter, partition::Co end if is_defined_const_binding(kind) + if kind == BINDING_KIND_BACKDATED_CONST + # Infer this as guard. We do not want a later const definition to retroactively improve + # inference results in an earlier world. + return RTEffects(Any, UndefVarError, generic_getglobal_effects) + end rt = Const(partition_restriction(partition)) return RTEffects(rt, Union{}, Effects(EFFECTS_TOTAL, inaccessiblememonly=is_mutation_free_argtype(rt) ? ALWAYS_TRUE : ALWAYS_FALSE)) end diff --git a/Compiler/src/ssair/slot2ssa.jl b/Compiler/src/ssair/slot2ssa.jl index 80dffdab23243..e0f3e207789a3 100644 --- a/Compiler/src/ssair/slot2ssa.jl +++ b/Compiler/src/ssair/slot2ssa.jl @@ -137,10 +137,6 @@ function fixemup!(@specialize(slot_filter), @specialize(rename_slot), ir::IRCode return nothing end op[] = x - elseif isa(val, GlobalRef) && !(isdefined(val.mod, val.name) && isconst(val.mod, val.name)) - typ = typ_for_val(val, ci, ir, idx, Any[]) - new_inst = NewInstruction(val, typ) - op[] = NewSSAValue(insert_node!(ir, idx, new_inst).id - length(ir.stmts)) elseif isexpr(val, :static_parameter) ty = typ_for_val(val, ci, ir, idx, Any[]) if isa(ty, Const) diff --git a/Compiler/src/ssair/verify.jl b/Compiler/src/ssair/verify.jl index fa16bdcc7ab19..779a28afe9f56 100644 --- a/Compiler/src/ssair/verify.jl +++ b/Compiler/src/ssair/verify.jl @@ -121,7 +121,7 @@ function verify_ir(ir::IRCode, print::Bool=true, if mi !== nothing push!(error_args, "\n", " Method instance: ", mi) end - error(error_args...) + invokelatest(error, error_args...) end # Verify CFG graph. Must be well formed to construct domtree 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, # undefined GlobalRef is OK in isdefined continue end + elseif stmt.head === :throw_undef_if_not + if length(stmt.args) > 3 + @verify_error "malformed throw_undef_if_not" + raise_error() + end + if stmt.args[1] isa GlobalRef + # undefined GlobalRef is OK in throw_undef_if_not + continue + end elseif stmt.head === :gc_preserve_end # We allow gc_preserve_end tokens to span across try/catch # blocks, which isn't allowed for regular SSA values, so diff --git a/Compiler/test/invalidation.jl b/Compiler/test/invalidation.jl index 2642c1647a682..b77c7677e6987 100644 --- a/Compiler/test/invalidation.jl +++ b/Compiler/test/invalidation.jl @@ -55,7 +55,7 @@ let mi = Base.method_instance(basic_caller, (Float64,)) end # this redefinition below should invalidate the cache -const BASIC_CALLER_WORLD = Base.get_world_counter() +const BASIC_CALLER_WORLD = Base.get_world_counter()+1 basic_callee(x) = x, x @test !isdefined(Base.method_instance(basic_callee, (Float64,)), :cache) let mi = Base.method_instance(basic_caller, (Float64,)) diff --git a/base/Base_compiler.jl b/base/Base_compiler.jl index db3ebb0232e38..abeec81f0c028 100644 --- a/base/Base_compiler.jl +++ b/base/Base_compiler.jl @@ -257,6 +257,10 @@ using .Order include("coreir.jl") include("invalidation.jl") +# Because lowering inserts direct references, it is mandatory for this binding +# to exist before we start inferring code. +function string end + # For OS specific stuff # We need to strcat things here, before strings are really defined function strcat(x::String, y::String) diff --git a/base/boot.jl b/base/boot.jl index 53e439d83ebe2..9b386f90d4abe 100644 --- a/base/boot.jl +++ b/base/boot.jl @@ -383,9 +383,10 @@ struct StackOverflowError <: Exception end struct UndefRefError <: Exception end struct UndefVarError <: Exception var::Symbol + world::UInt 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) - UndefVarError(var::Symbol) = new(var) - UndefVarError(var::Symbol, @nospecialize scope) = new(var, scope) + UndefVarError(var::Symbol) = new(var, ccall(:jl_get_tls_world_age, UInt, ())) + UndefVarError(var::Symbol, @nospecialize scope) = new(var, ccall(:jl_get_tls_world_age, UInt, ()), scope) end struct ConcurrencyViolationError <: Exception msg::AbstractString @@ -717,7 +718,8 @@ macro __doc__(x) end isbasicdoc(@nospecialize x) = (isa(x, Expr) && x.head === :.) || isa(x, Union{QuoteNode, Symbol}) -iscallexpr(ex::Expr) = (isa(ex, Expr) && ex.head === :where) ? iscallexpr(ex.args[1]) : (isa(ex, Expr) && ex.head === :call) +firstarg(arg1, args...) = arg1 +iscallexpr(ex::Expr) = (isa(ex, Expr) && ex.head === :where) ? iscallexpr(firstarg(ex.args...)) : (isa(ex, Expr) && ex.head === :call) iscallexpr(ex) = false function ignoredoc(source, mod, str, expr) (isbasicdoc(expr) || iscallexpr(expr)) && return Expr(:escape, nothing) diff --git a/base/docs/bindings.jl b/base/docs/bindings.jl index 6095d52a28e5a..5c65a35659f81 100644 --- a/base/docs/bindings.jl +++ b/base/docs/bindings.jl @@ -16,8 +16,8 @@ end bindingexpr(x) = Expr(:call, Binding, splitexpr(x)...) -defined(b::Binding) = isdefined(b.mod, b.var) -resolve(b::Binding) = getfield(b.mod, b.var) +defined(b::Binding) = invokelatest(isdefined, b.mod, b.var) +resolve(b::Binding) = invokelatest(getfield, b.mod, b.var) function splitexpr(x::Expr) isexpr(x, :macrocall) ? splitexpr(x.args[1]) : diff --git a/base/runtime_internals.jl b/base/runtime_internals.jl index 964e8063dd5af..b61e24c11f3f9 100644 --- a/base/runtime_internals.jl +++ b/base/runtime_internals.jl @@ -229,8 +229,9 @@ const BINDING_KIND_FAILED = 0x6 const BINDING_KIND_DECLARED = 0x7 const BINDING_KIND_GUARD = 0x8 const BINDING_KIND_UNDEF_CONST = 0x9 +const BINDING_KIND_BACKDATED_CONST = 0xa -is_defined_const_binding(kind::UInt8) = (kind == BINDING_KIND_CONST || kind == BINDING_KIND_CONST_IMPORT) +is_defined_const_binding(kind::UInt8) = (kind == BINDING_KIND_CONST || kind == BINDING_KIND_CONST_IMPORT || kind == BINDING_KIND_BACKDATED_CONST) is_some_const_binding(kind::UInt8) = (is_defined_const_binding(kind) || kind == BINDING_KIND_UNDEF_CONST) is_some_imported(kind::UInt8) = (kind == BINDING_KIND_IMPLICIT || kind == BINDING_KIND_EXPLICIT || kind == BINDING_KIND_IMPORTED) is_some_guard(kind::UInt8) = (kind == BINDING_KIND_GUARD || kind == BINDING_KIND_DECLARED || kind == BINDING_KIND_FAILED || kind == BINDING_KIND_UNDEF_CONST) diff --git a/base/show.jl b/base/show.jl index de45ca07e3131..42788f05eceb5 100644 --- a/base/show.jl +++ b/base/show.jl @@ -35,7 +35,7 @@ function _isself(ft::DataType) isdefined(ftname, :mt) || return false name = ftname.mt.name mod = parentmodule(ft) # NOTE: not necessarily the same as ft.name.mt.module - return isdefined(mod, name) && ft == typeof(getfield(mod, name)) + return invokelatest(isdefinedglobal, mod, name) && ft == typeof(invokelatest(getglobal, mod, name)) end function show(io::IO, ::MIME"text/plain", f::Function) @@ -542,8 +542,8 @@ function show_function(io::IO, f::Function, compact::Bool, fallback::Function) fallback(io, f) elseif compact print(io, mt.name) - elseif isdefined(mt, :module) && isdefined(mt.module, mt.name) && - getfield(mt.module, mt.name) === f + elseif isdefined(mt, :module) && isdefinedglobal(mt.module, mt.name) && + getglobal(mt.module, mt.name) === f # this used to call the removed internal function `is_exported_from_stdlib`, which effectively # just checked for exports from Core and Base. mod = get(io, :module, UsesCoreAndBaseOnly) @@ -1025,15 +1025,15 @@ function isvisible(sym::Symbol, parent::Module, from::Module) from_owner = ccall(:jl_binding_owner, Ptr{Cvoid}, (Any, Any), from, sym) return owner !== C_NULL && from_owner === owner && !isdeprecated(parent, sym) && - isdefined(from, sym) # if we're going to return true, force binding resolution + isdefinedglobal(from, sym) # if we're going to return true, force binding resolution end function is_global_function(tn::Core.TypeName, globname::Union{Symbol,Nothing}) if globname !== nothing globname_str = string(globname::Symbol) if ('#' ∉ globname_str && '@' ∉ globname_str && isdefined(tn, :module) && - isbindingresolved(tn.module, globname) && isdefined(tn.module, globname) && - isconcretetype(tn.wrapper) && isa(getfield(tn.module, globname), tn.wrapper)) + isbindingresolved(tn.module, globname) && isdefinedglobal(tn.module, globname) && + isconcretetype(tn.wrapper) && isa(getglobal(tn.module, globname), tn.wrapper)) return true end end @@ -3364,7 +3364,10 @@ function print_partition(io::IO, partition::Core.BindingPartition) end print(io, " - ") kind = binding_kind(partition) - if is_defined_const_binding(kind) + if kind == BINDING_KIND_BACKDATED_CONST + print(io, "backdated constant binding to ") + print(io, partition_restriction(partition)) + elseif is_defined_const_binding(kind) print(io, "constant binding to ") print(io, partition_restriction(partition)) elseif kind == BINDING_KIND_UNDEF_CONST diff --git a/doc/src/manual/methods.md b/doc/src/manual/methods.md index 3c234b17f10d8..e448c62465b0d 100644 --- a/doc/src/manual/methods.md +++ b/doc/src/manual/methods.md @@ -587,12 +587,13 @@ In the example above, we see that the "current world" (in which the method `newf is one greater than the task-local "runtime world" that was fixed when the execution of `tryeval` started. Sometimes it is necessary to get around this (for example, if you are implementing the above REPL). -Fortunately, there is an easy solution: call the function using [`Base.invokelatest`](@ref): +Fortunately, there is an easy solution: call the function using [`Base.invokelatest`](@ref) or +the macro version [`Base.@invokelatest`](@ref): ```jldoctest julia> function tryeval2() @eval newfun2() = 2 - Base.invokelatest(newfun2) + @invokelatest newfun2() end tryeval2 (generic function with 1 method) diff --git a/src/codegen.cpp b/src/codegen.cpp index 60e53f75638ed..19ee1e9161152 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -3469,7 +3469,8 @@ static jl_cgval_t emit_globalref(jl_codectx_t &ctx, jl_module_t *mod, jl_sym_t * break; pku = jl_atomic_load_acquire(&bpart->restriction); } - if (bpart && jl_bkind_is_some_constant(decode_restriction_kind(pku))) { + enum jl_partition_kind kind = decode_restriction_kind(pku); + if (bpart && (jl_bkind_is_some_constant(kind) && kind != BINDING_KIND_BACKDATED_CONST)) { jl_value_t *constval = decode_restriction_value(pku); if (!constval) { undef_var_error_ifnot(ctx, ConstantInt::get(getInt1Ty(ctx.builder.getContext()), 0), name, (jl_value_t*)mod); diff --git a/src/julia.h b/src/julia.h index 1088f669ad773..f36133088119f 100644 --- a/src/julia.h +++ b/src/julia.h @@ -647,7 +647,10 @@ enum jl_partition_kind { // Undef Constant: This binding partition is a constant declared using `const`, but // without a value. // ->restriction is NULL - BINDING_KIND_UNDEF_CONST = 0x9 + BINDING_KIND_UNDEF_CONST = 0x9, + // Backated constant. A constant that was backdated for compatibility. In all other + // ways equivalent to BINDING_KIND_CONST, but prints a warning on access + BINDING_KIND_BACKDATED_CONST = 0xa, }; #ifdef _P64 @@ -693,7 +696,7 @@ typedef struct _jl_binding_t { jl_globalref_t *globalref; // cached GlobalRef for this binding _Atomic(jl_value_t*) value; _Atomic(jl_binding_partition_t*) partitions; - uint8_t declared:1; + uint8_t did_print_backdate_admonition:1; uint8_t exportp:1; // `public foo` sets `publicp`, `export foo` sets both `publicp` and `exportp` uint8_t publicp:1; // exportp without publicp is not allowed. uint8_t deprecated:2; // 0=not deprecated, 1=renamed, 2=moved to another package @@ -2025,10 +2028,6 @@ JL_DLLEXPORT void jl_module_public(jl_module_t *from, jl_sym_t *s, int exported) JL_DLLEXPORT int jl_is_imported(jl_module_t *m, jl_sym_t *s); JL_DLLEXPORT int jl_module_exports_p(jl_module_t *m, jl_sym_t *var); JL_DLLEXPORT void jl_add_standard_imports(jl_module_t *m); -STATIC_INLINE jl_function_t *jl_get_function(jl_module_t *m, const char *name) -{ - return (jl_function_t*)jl_get_global(m, jl_symbol(name)); -} // eq hash tables JL_DLLEXPORT jl_genericmemory_t *jl_eqtable_put(jl_genericmemory_t *h JL_ROOTING_ARGUMENT, jl_value_t *key, jl_value_t *val JL_ROOTED_ARGUMENT, int *inserted); @@ -2590,9 +2589,18 @@ typedef struct { } jl_nullable_float32_t; #define jl_root_task (jl_current_task->ptls->root_task) - JL_DLLEXPORT jl_task_t *jl_get_current_task(void) JL_GLOBALLY_ROOTED JL_NOTSAFEPOINT; +STATIC_INLINE jl_function_t *jl_get_function(jl_module_t *m, const char *name) +{ + jl_task_t *ct = jl_get_current_task(); + size_t last_world = ct->world_age; + ct->world_age = jl_get_world_counter(); + jl_value_t *r = jl_get_global(m, jl_symbol(name)); + ct->world_age = last_world; + return (jl_function_t*)r; +} + // TODO: we need to pin the task while using this (set pure bit) JL_DLLEXPORT jl_jmp_buf *jl_get_safe_restore(void) JL_NOTSAFEPOINT; JL_DLLEXPORT void jl_set_safe_restore(jl_jmp_buf *) JL_NOTSAFEPOINT; diff --git a/src/julia_internal.h b/src/julia_internal.h index 0da6d412c8a49..a838b75e506a2 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -933,6 +933,10 @@ EXTERN_INLINE_DECLARE enum jl_partition_kind decode_restriction_kind(jl_ptr_kind if (bits == BINDING_KIND_CONST) { return BINDING_KIND_UNDEF_CONST; } + } else { + if (bits == BINDING_KIND_DECLARED) { + return BINDING_KIND_BACKDATED_CONST; + } } return (enum jl_partition_kind)bits; @@ -956,12 +960,14 @@ STATIC_INLINE jl_ptr_kind_union_t encode_restriction(jl_value_t *val, enum jl_pa #ifdef _P64 if (kind == BINDING_KIND_GUARD || kind == BINDING_KIND_DECLARED || kind == BINDING_KIND_FAILED || kind == BINDING_KIND_UNDEF_CONST) assert(val == NULL); - else if (kind == BINDING_KIND_IMPLICIT || kind == BINDING_KIND_CONST) + else if (kind == BINDING_KIND_IMPLICIT || kind == BINDING_KIND_CONST || kind == BINDING_KIND_BACKDATED_CONST) assert(val != NULL); if (kind == BINDING_KIND_GUARD) kind = BINDING_KIND_IMPLICIT; else if (kind == BINDING_KIND_UNDEF_CONST) kind = BINDING_KIND_CONST; + else if (kind == BINDING_KIND_BACKDATED_CONST) + kind = BINDING_KIND_DECLARED; assert((((uintptr_t)val) & 0x7) == 0); return ((jl_ptr_kind_union_t)val) | kind; #else @@ -975,11 +981,11 @@ STATIC_INLINE int jl_bkind_is_some_import(enum jl_partition_kind kind) JL_NOTSAF } STATIC_INLINE int jl_bkind_is_some_constant(enum jl_partition_kind kind) JL_NOTSAFEPOINT { - return kind == BINDING_KIND_CONST || kind == BINDING_KIND_CONST_IMPORT || kind == BINDING_KIND_UNDEF_CONST; + return kind == BINDING_KIND_CONST || kind == BINDING_KIND_CONST_IMPORT || kind == BINDING_KIND_UNDEF_CONST || kind == BINDING_KIND_BACKDATED_CONST; } STATIC_INLINE int jl_bkind_is_defined_constant(enum jl_partition_kind kind) JL_NOTSAFEPOINT { - return kind == BINDING_KIND_CONST || kind == BINDING_KIND_CONST_IMPORT; + return kind == BINDING_KIND_CONST || kind == BINDING_KIND_CONST_IMPORT || kind == BINDING_KIND_BACKDATED_CONST; } STATIC_INLINE int jl_bkind_is_some_guard(enum jl_partition_kind kind) JL_NOTSAFEPOINT { diff --git a/src/module.c b/src/module.c index be6779727bfdc..f4c56e19efa61 100644 --- a/src/module.c +++ b/src/module.c @@ -241,6 +241,7 @@ static jl_binding_t *new_binding(jl_module_t *mod, jl_sym_t *name) b->exportp = 0; b->publicp = 0; b->deprecated = 0; + b->did_print_backdate_admonition = 0; JL_GC_PUSH1(&b); b->globalref = jl_new_globalref(mod, name, b); jl_gc_wb(b, b->globalref); @@ -322,14 +323,37 @@ JL_DLLEXPORT jl_module_t *jl_get_module_of_binding(jl_module_t *m, jl_sym_t *var return b->globalref->mod; // TODO: deprecate this? } +static NOINLINE void print_backdate_admonition(jl_binding_t *b) JL_NOTSAFEPOINT +{ + jl_safe_printf( + "WARNING: Detected access to binding `%s.%s` in a world prior to its definition world.\n" + " Julia 1.12 has introduced more strict world age semantics for global bindings.\n" + " !!! This code may malfunction under Revise.\n" + " !!! This code will error in future versions of Julia.\n" + "Hint: Add an appropriate `invokelatest` around the access to this binding.\n", + jl_symbol_name(b->globalref->mod->name), jl_symbol_name(b->globalref->name)); + b->did_print_backdate_admonition = 1; +} + +static inline void check_backdated_binding(jl_binding_t *b, enum jl_partition_kind kind) JL_NOTSAFEPOINT +{ + if (__unlikely(kind == BINDING_KIND_BACKDATED_CONST) && + !b->did_print_backdate_admonition) { + print_backdate_admonition(b); + } +} + JL_DLLEXPORT jl_value_t *jl_get_binding_value(jl_binding_t *b) { jl_binding_partition_t *bpart = jl_get_binding_partition(b, jl_current_task->world_age); jl_ptr_kind_union_t pku = jl_walk_binding_inplace(&b, &bpart, jl_current_task->world_age); - if (jl_bkind_is_some_guard(decode_restriction_kind(pku))) + enum jl_partition_kind kind = decode_restriction_kind(pku); + if (jl_bkind_is_some_guard(kind)) return NULL; - if (jl_bkind_is_some_constant(decode_restriction_kind(pku))) + if (jl_bkind_is_some_constant(kind)) { + check_backdated_binding(b, kind); return decode_restriction_value(pku); + } return jl_atomic_load_relaxed(&b->value); } @@ -337,10 +361,13 @@ JL_DLLEXPORT jl_value_t *jl_get_binding_value_seqcst(jl_binding_t *b) { jl_binding_partition_t *bpart = jl_get_binding_partition(b, jl_current_task->world_age); jl_ptr_kind_union_t pku = jl_walk_binding_inplace(&b, &bpart, jl_current_task->world_age); - if (jl_bkind_is_some_guard(decode_restriction_kind(pku))) + enum jl_partition_kind kind = decode_restriction_kind(pku); + if (jl_bkind_is_some_guard(kind)) return NULL; - if (jl_bkind_is_some_constant(decode_restriction_kind(pku))) + if (jl_bkind_is_some_constant(kind)) { + check_backdated_binding(b, kind); return decode_restriction_value(pku); + } return jl_atomic_load(&b->value); } @@ -348,10 +375,12 @@ JL_DLLEXPORT jl_value_t *jl_get_binding_value_if_const(jl_binding_t *b) { jl_binding_partition_t *bpart = jl_get_binding_partition(b, jl_current_task->world_age); jl_ptr_kind_union_t pku = jl_walk_binding_inplace(&b, &bpart, jl_current_task->world_age); - if (jl_bkind_is_some_guard(decode_restriction_kind(pku))) + enum jl_partition_kind kind = decode_restriction_kind(pku); + if (jl_bkind_is_some_guard(kind)) return NULL; - if (!jl_bkind_is_some_constant(decode_restriction_kind(pku))) + if (!jl_bkind_is_some_constant(kind)) return NULL; + check_backdated_binding(b, kind); return decode_restriction_value(pku); } @@ -368,10 +397,12 @@ JL_DLLEXPORT jl_value_t *jl_get_binding_value_if_resolved_and_const(jl_binding_t if (bpart->min_world > jl_current_task->world_age || jl_current_task->world_age > max_world) return NULL; jl_ptr_kind_union_t pku = jl_atomic_load_relaxed(&bpart->restriction); - if (jl_bkind_is_some_guard(decode_restriction_kind(pku))) + enum jl_partition_kind kind = decode_restriction_kind(pku); + if (jl_bkind_is_some_guard(kind)) return NULL; - if (!jl_bkind_is_some_constant(decode_restriction_kind(pku))) + if (!jl_bkind_is_some_constant(kind)) return NULL; + check_backdated_binding(b, kind); return decode_restriction_value(pku); } @@ -388,12 +419,15 @@ JL_DLLEXPORT jl_value_t *jl_get_binding_value_if_resolved(jl_binding_t *b) if (bpart->min_world > jl_current_task->world_age || jl_current_task->world_age > max_world) return NULL; jl_ptr_kind_union_t pku = jl_atomic_load_relaxed(&bpart->restriction); - if (jl_bkind_is_some_guard(decode_restriction_kind(pku))) + enum jl_partition_kind kind = decode_restriction_kind(pku); + if (jl_bkind_is_some_guard(kind)) return NULL; - if (jl_bkind_is_some_import(decode_restriction_kind(pku))) + if (jl_bkind_is_some_import(kind)) return NULL; - if (jl_bkind_is_some_constant(decode_restriction_kind(pku))) + if (jl_bkind_is_some_constant(kind)) { + check_backdated_binding(b, kind); return decode_restriction_value(pku); + } return jl_atomic_load_relaxed(&b->value); } @@ -895,13 +929,26 @@ JL_DLLEXPORT int jl_boundp(jl_module_t *m, jl_sym_t *var, int allow_import) // u jl_binding_t *b = jl_get_module_binding(m, var, allow_import); if (!b) return 0; + jl_binding_partition_t *bpart = jl_get_binding_partition(b, jl_current_task->world_age); + if (!bpart) + return 0; + jl_ptr_kind_union_t pku = jl_atomic_load_relaxed(&bpart->restriction); if (!allow_import) { - jl_binding_partition_t *bpart = jl_get_binding_partition(b, jl_current_task->world_age); - if (!bpart || jl_bkind_is_some_import(decode_restriction_kind(jl_atomic_load_relaxed(&bpart->restriction)))) + if (!bpart || jl_bkind_is_some_import(decode_restriction_kind(pku))) return 0; - return jl_get_binding_value(b) != NULL; + } else { + if (jl_bkind_is_some_guard(decode_restriction_kind(pku))) { + jl_resolve_owner(b, b->globalref->mod, b->globalref->name, NULL, jl_current_task->world_age); + } + pku = jl_walk_binding_inplace(&b, &bpart, jl_current_task->world_age); + } + if (jl_bkind_is_some_guard(decode_restriction_kind(pku))) + return 0; + if (jl_bkind_is_defined_constant(decode_restriction_kind(pku))) { + // N.B.: No backdated check for isdefined + return 1; } - return jl_reresolve_binding_value_seqcst(b) != NULL; + return jl_atomic_load(&b->value) != NULL; } JL_DLLEXPORT int jl_defines_or_exports_p(jl_module_t *m, jl_sym_t *var) diff --git a/src/rtutils.c b/src/rtutils.c index 00a5b639d8683..6515b80c5d2b5 100644 --- a/src/rtutils.c +++ b/src/rtutils.c @@ -148,8 +148,10 @@ JL_DLLEXPORT void JL_NORETURN jl_undefined_var_error(jl_sym_t *var, jl_value_t * } jl_errorf("UndefVarError(%s%s%s)", jl_symbol_name(var), s1, s2); } - JL_GC_PUSH1(&scope); - jl_throw(jl_new_struct(jl_undefvarerror_type, var, scope)); + jl_value_t *active_age = NULL; + JL_GC_PUSH2(&scope, &active_age); + active_age = jl_box_long(jl_current_task->world_age); + jl_throw(jl_new_struct(jl_undefvarerror_type, var, active_age, scope)); } JL_DLLEXPORT void JL_NORETURN jl_has_no_field_error(jl_datatype_t *t, jl_sym_t *var) diff --git a/src/toplevel.c b/src/toplevel.c index dee9029e2feb7..30e78bf813fc8 100644 --- a/src/toplevel.c +++ b/src/toplevel.c @@ -669,7 +669,7 @@ static void import_module(jl_module_t *JL_NONNULL m, jl_module_t *import, jl_sym if (decode_restriction_kind(pku) != BINDING_KIND_GUARD && decode_restriction_kind(pku) != BINDING_KIND_FAILED) { // Unlike regular constant declaration, we allow this as long as we eventually end up at a constant. pku = jl_walk_binding_inplace(&b, &bpart, jl_current_task->world_age); - if (decode_restriction_kind(pku) == BINDING_KIND_CONST || decode_restriction_kind(pku) == BINDING_KIND_CONST_IMPORT) { + if (decode_restriction_kind(pku) == BINDING_KIND_CONST || decode_restriction_kind(pku) == BINDING_KIND_BACKDATED_CONST || decode_restriction_kind(pku) == BINDING_KIND_CONST_IMPORT) { // Already declared (e.g. on another thread) or imported. if (decode_restriction_value(pku) == (jl_value_t*)import) return; @@ -770,6 +770,12 @@ JL_DLLEXPORT jl_binding_partition_t *jl_declare_constant_val3( jl_symbol_name(var)); did_warn = 1; } + if (new_world > bpart->min_world) { + // TODO: Invoke invalidation logic here + jl_atomic_store_relaxed(&bpart->max_world, new_world - 1); + bpart = jl_get_binding_partition(b, new_world); + pku = jl_atomic_load_relaxed(&bpart->restriction); + } } else if (!jl_bkind_is_some_guard(decode_restriction_kind(pku))) { if (jl_bkind_is_some_import(decode_restriction_kind(pku))) { jl_errorf("cannot declare %s.%s constant; it was already declared as an import", @@ -779,15 +785,16 @@ JL_DLLEXPORT jl_binding_partition_t *jl_declare_constant_val3( jl_symbol_name(mod->name), jl_symbol_name(var)); } } - if (jl_atomic_cmpswap(&bpart->restriction, &pku, encode_restriction(val, constant_kind))) { - jl_gc_wb(bpart, val); - break; + if (!jl_atomic_cmpswap(&bpart->restriction, &pku, encode_restriction(val, constant_kind))) { + continue; } - } - // N.B.: This backdates the first definition of the constant to world age 0 for backwards compatibility - // TODO: Mark this specially with a separate partition. - if (bpart->min_world != 0) + jl_gc_wb(bpart, val); + int needs_backdate = bpart->min_world == 0 && new_world && val; bpart->min_world = new_world; + if (needs_backdate) { + jl_declare_constant_val3(b, mod, var, val, BINDING_KIND_BACKDATED_CONST, 0); + } + } JL_GC_POP(); return bpart; } diff --git a/stdlib/REPL/src/REPL.jl b/stdlib/REPL/src/REPL.jl index 6c3f4bd4ba73a..f83fa867748af 100644 --- a/stdlib/REPL/src/REPL.jl +++ b/stdlib/REPL/src/REPL.jl @@ -33,9 +33,9 @@ function UndefVarError_hint(io::IO, ex::UndefVarError) if isdefined(ex, :scope) scope = ex.scope if scope isa Module - bpart = Base.lookup_binding_partition(Base.get_world_counter(), GlobalRef(scope, var)) + bpart = Base.lookup_binding_partition(ex.world, GlobalRef(scope, var)) kind = Base.binding_kind(bpart) - if kind === Base.BINDING_KIND_GLOBAL || kind === Base.BINDING_KIND_CONST || kind == Base.BINDING_KIND_DECLARED + if kind === Base.BINDING_KIND_GLOBAL || kind === Base.BINDING_KIND_UNDEF_CONST || kind == Base.BINDING_KIND_DECLARED print(io, "\nSuggestion: add an appropriate import or assignment. This global was declared but not assigned.") elseif kind === Base.BINDING_KIND_FAILED print(io, "\nHint: It looks like two or more modules export different ", diff --git a/stdlib/Test/src/Test.jl b/stdlib/Test/src/Test.jl index 79fdb89399d42..88fd055f7bd58 100644 --- a/stdlib/Test/src/Test.jl +++ b/stdlib/Test/src/Test.jl @@ -783,6 +783,19 @@ end const MACROEXPAND_LIKE = Symbol.(("@macroexpand", "@macroexpand1", "macroexpand")) +function isequalexception(@nospecialize(a), @nospecialize(b)) + for fld in 1:nfields(b) + if !isequal(getfield(a, fld), getfield(b, fld)) + return false + end + end + return true +end +function isequalexception(a::UndefVarError, b::UndefVarError) + # Ignore different world ages + return isequal(a.var, b.var) && isequal(a.scope, b.scope) +end + # An internal function, called by the code generated by @test_throws # to evaluate and catch the thrown exception - if it exists function do_test_throws(result::ExecutionResult, orig_expr, extype) @@ -817,13 +830,7 @@ function do_test_throws(result::ExecutionResult, orig_expr, extype) if isa(extype, UndefVarError) && !isdefined(extype, :scope) success = exc isa UndefVarError && exc.var == extype.var else isa(exc, typeof(extype)) - success = true - for fld in 1:nfields(extype) - if !isequal(getfield(extype, fld), getfield(exc, fld)) - success = false - break - end - end + success = isequalexception(exc, extype) end else message_only = true diff --git a/test/worlds.jl b/test/worlds.jl index 8bc96f8303aef..025aaba6cea4f 100644 --- a/test/worlds.jl +++ b/test/worlds.jl @@ -115,15 +115,15 @@ wc265_41332a = Task(tls_world_age) global wc265_41332d = Task(tls_world_age) nothing end)() -@test wc265 + 3 == get_world_counter() == tls_world_age() +@test wc265 + 4 == get_world_counter() == tls_world_age() schedule(wc265_41332a) schedule(wc265_41332b) schedule(wc265_41332c) schedule(wc265_41332d) @test wc265 == fetch(wc265_41332a) -@test wc265 + 1 == fetch(wc265_41332b) -@test wc265 + 3 == fetch(wc265_41332c) -@test wc265 + 1 == fetch(wc265_41332d) +@test wc265 + 2 == fetch(wc265_41332b) +@test wc265 + 4 == fetch(wc265_41332c) +@test wc265 + 2 == fetch(wc265_41332d) chnls, tasks = Base.channeled_tasks(2, wfunc) t265 = tasks[1] From 0625b3a0a661b869b24bb025b5ce2d5db94e8040 Mon Sep 17 00:00:00 2001 From: Kiran Pamnany Date: Sun, 26 Jan 2025 19:47:07 -0500 Subject: [PATCH 28/56] Add `Experimental.wait_with_timeout` (#57148) Remove the `timeout` parameter to `wait(::Condition)` added in https://github.com/JuliaLang/julia/pull/56974 and add this logic to `Experimental.wait_with_timeout`. --------- Co-authored-by: Jameson Nash Co-authored-by: Nick Robinson --- base/condition.jl | 90 ++--------------------------------- base/experimental.jl | 109 +++++++++++++++++++++++++++++++++++++++++++ test/channels.jl | 7 ++- 3 files changed, 115 insertions(+), 91 deletions(-) diff --git a/base/condition.jl b/base/condition.jl index 90c53b7ad310d..fd771c9be346a 100644 --- a/base/condition.jl +++ b/base/condition.jl @@ -125,104 +125,20 @@ proceeding. """ function wait end -# wait with timeout -# -# The behavior of wait changes if a timeout is specified. There are -# three concurrent entities that can interact: -# 1. Task W: the task that calls wait w/timeout. -# 2. Task T: the task created to handle a timeout. -# 3. Task N: the task that notifies the Condition being waited on. -# -# Typical flow: -# - W enters the Condition's wait queue. -# - W creates T and stops running (calls wait()). -# - T, when scheduled, waits on a Timer. -# - Two common outcomes: -# - N notifies the Condition. -# - W starts running, closes the Timer, sets waiter_left and returns -# the notify'ed value. -# - The closed Timer throws an EOFError to T which simply ends. -# - The Timer expires. -# - T starts running and locks the Condition. -# - T confirms that waiter_left is unset and that W is still in the -# Condition's wait queue; it then removes W from the wait queue, -# sets dosched to true and unlocks the Condition. -# - If dosched is true, T schedules W with the special :timed_out -# value. -# - T ends. -# - W runs and returns :timed_out. -# -# Some possible interleavings: -# - N notifies the Condition but the Timer expires and T starts running -# before W: -# - W closing the expired Timer is benign. -# - T will find that W is no longer in the Condition's wait queue -# (which is protected by a lock) and will not schedule W. -# - N notifies the Condition; W runs and calls wait on the Condition -# again before the Timer expires: -# - W sets waiter_left before leaving. When T runs, it will find that -# waiter_left is set and will not schedule W. -# -# The lock on the Condition's wait queue and waiter_left together -# ensure proper synchronization and behavior of the tasks involved. - """ - wait(c::GenericCondition; first::Bool=false, timeout::Real=0.0) + wait(c::GenericCondition; first::Bool=false) Wait for [`notify`](@ref) on `c` and return the `val` parameter passed to `notify`. If the keyword `first` is set to `true`, the waiter will be put _first_ in line to wake up on `notify`. Otherwise, `wait` has first-in-first-out (FIFO) behavior. - -If `timeout` is specified, cancel the `wait` when it expires and return -`:timed_out`. The minimum value for `timeout` is 0.001 seconds, i.e. 1 -millisecond. """ -function wait(c::GenericCondition; first::Bool=false, timeout::Real=0.0) - timeout == 0.0 || timeout ≥ 1e-3 || throw(ArgumentError("timeout must be ≥ 1 millisecond")) - +function wait(c::GenericCondition; first::Bool=false) ct = current_task() _wait2(c, ct, first) token = unlockall(c.lock) - - timer::Union{Timer, Nothing} = nothing - waiter_left::Union{Threads.Atomic{Bool}, Nothing} = nothing - if timeout > 0.0 - timer = Timer(timeout) - waiter_left = Threads.Atomic{Bool}(false) - # start a task to wait on the timer - t = Task() do - try - wait(timer) - catch e - # if the timer was closed, the waiting task has been scheduled; do nothing - e isa EOFError && return - end - dosched = false - lock(c.lock) - # Confirm that the waiting task is still in the wait queue and remove it. If - # the task is not in the wait queue, it must have been notified already so we - # don't do anything here. - if !waiter_left[] && ct.queue == c.waitq - dosched = true - Base.list_deletefirst!(c.waitq, ct) - end - unlock(c.lock) - # send the waiting task a timeout - dosched && schedule(ct, :timed_out) - end - t.sticky = false - Threads._spawn_set_thrpool(t, :interactive) - schedule(t) - end - try - res = wait() - if timer !== nothing - close(timer) - waiter_left[] = true - end - return res + return wait() catch q = ct.queue; q === nothing || Base.list_deletefirst!(q::IntrusiveLinkedList{Task}, ct) rethrow() diff --git a/base/experimental.jl b/base/experimental.jl index 17871b4f346d6..e35e920298c3d 100644 --- a/base/experimental.jl +++ b/base/experimental.jl @@ -10,6 +10,7 @@ module Experimental using Base: Threads, sync_varname, is_function_def, @propagate_inbounds +using Base: GenericCondition using Base.Meta """ @@ -577,4 +578,112 @@ function task_wall_time_ns(t::Task=current_task()) return end_at - start_at end +# wait_with_timeout +# +# A version of `wait(c::Condition)` that additionally allows the +# specification of a timeout. This is experimental as it will likely +# be dropped when a cancellation framework is added. +# +# The parallel behavior of wait_with_timeout is specified here. There +# are three concurrent entities that can interact: +# 1. Task W: the task that calls wait_with_timeout. +# 2. Task T: the task created to handle a timeout. +# 3. Task N: the task that notifies the Condition being waited on. +# +# Typical flow: +# - W enters the Condition's wait queue. +# - W creates T and stops running (calls wait()). +# - T, when scheduled, waits on a Timer. +# - Two common outcomes: +# - N notifies the Condition. +# - W starts running, closes the Timer, sets waiter_left and returns +# the notify'ed value. +# - The closed Timer throws an EOFError to T which simply ends. +# - The Timer expires. +# - T starts running and locks the Condition. +# - T confirms that waiter_left is unset and that W is still in the +# Condition's wait queue; it then removes W from the wait queue, +# sets dosched to true and unlocks the Condition. +# - If dosched is true, T schedules W with the special :timed_out +# value. +# - T ends. +# - W runs and returns :timed_out. +# +# Some possible interleavings: +# - N notifies the Condition but the Timer expires and T starts running +# before W: +# - W closing the expired Timer is benign. +# - T will find that W is no longer in the Condition's wait queue +# (which is protected by a lock) and will not schedule W. +# - N notifies the Condition; W runs and calls wait on the Condition +# again before the Timer expires: +# - W sets waiter_left before leaving. When T runs, it will find that +# waiter_left is set and will not schedule W. +# +# The lock on the Condition's wait queue and waiter_left together +# ensure proper synchronization and behavior of the tasks involved. + +""" + wait_with_timeout(c::GenericCondition; first::Bool=false, timeout::Real=0.0) + +Wait for [`notify`](@ref) on `c` and return the `val` parameter passed to `notify`. + +If the keyword `first` is set to `true`, the waiter will be put _first_ +in line to wake up on `notify`. Otherwise, `wait` has first-in-first-out (FIFO) behavior. + +If `timeout` is specified, cancel the `wait` when it expires and return +`:timed_out`. The minimum value for `timeout` is 0.001 seconds, i.e. 1 +millisecond. +""" +function wait_with_timeout(c::GenericCondition; first::Bool=false, timeout::Real=0.0) + ct = current_task() + Base._wait2(c, ct, first) + token = Base.unlockall(c.lock) + + timer::Union{Timer, Nothing} = nothing + waiter_left::Union{Threads.Atomic{Bool}, Nothing} = nothing + if timeout > 0.0 + timer = Timer(timeout) + waiter_left = Threads.Atomic{Bool}(false) + # start a task to wait on the timer + t = Task() do + try + wait(timer) + catch e + # if the timer was closed, the waiting task has been scheduled; do nothing + e isa EOFError && return + end + dosched = false + lock(c.lock) + # Confirm that the waiting task is still in the wait queue and remove it. If + # the task is not in the wait queue, it must have been notified already so we + # don't do anything here. + if !waiter_left[] && ct.queue == c.waitq + dosched = true + Base.list_deletefirst!(c.waitq, ct) + end + unlock(c.lock) + # send the waiting task a timeout + dosched && schedule(ct, :timed_out) + end + t.sticky = false + Threads._spawn_set_thrpool(t, :interactive) + schedule(t) + end + + try + res = wait() + if timer !== nothing + close(timer) + waiter_left[] = true + end + return res + catch + q = ct.queue; q === nothing || Base.list_deletefirst!(q::IntrusiveLinkedList{Task}, ct) + rethrow() + finally + Base.relockall(c.lock, token) + end +end + end # module diff --git a/test/channels.jl b/test/channels.jl index 6e74a2079234c..d654bc63be586 100644 --- a/test/channels.jl +++ b/test/channels.jl @@ -40,16 +40,15 @@ end @test fetch(t) == "finished" end -@testset "timed wait on Condition" begin +@testset "wait_with_timeout on Condition" begin a = Threads.Condition() - @test_throws ArgumentError @lock a wait(a; timeout=0.0005) - @test @lock a wait(a; timeout=0.1)==:timed_out + @test @lock a Experimental.wait_with_timeout(a; timeout=0.1)==:timed_out lock(a) @spawn begin @lock a notify(a) end @test try - wait(a; timeout=2) + Experimental.wait_with_timeout(a; timeout=2) true finally unlock(a) From 47a6ad0b2adbcee14274a1c1375d3940745ef4ba Mon Sep 17 00:00:00 2001 From: Eduardo Souza Date: Tue, 28 Jan 2025 03:47:20 +1100 Subject: [PATCH 29/56] Adding stock gc heap resizing heuristics to MMTk (#57147) This PR sets MMTk to use the stock GC heuristics for heap resizing by default. It also updates the `mmtk-julia` version to incorporate the changes that support the new gc triggering heuristics (see https://github.com/mmtk/mmtk-julia/pull/222). --- contrib/refresh_checksums.mk | 2 +- deps/checksums/mmtk_julia | 4 +++ deps/mmtk_julia.version | 6 ++-- src/gc-mmtk.c | 54 ++++++++++++++++++++++++++++-------- 4 files changed, 50 insertions(+), 16 deletions(-) diff --git a/contrib/refresh_checksums.mk b/contrib/refresh_checksums.mk index 5a787b0b67cb1..fa7cddc705958 100644 --- a/contrib/refresh_checksums.mk +++ b/contrib/refresh_checksums.mk @@ -24,7 +24,7 @@ CLANG_TRIPLETS=$(filter %-darwin %-freebsd,$(TRIPLETS)) NON_CLANG_TRIPLETS=$(filter-out %-darwin %-freebsd,$(TRIPLETS)) # These are the projects currently using BinaryBuilder; both GCC-expanded and non-GCC-expanded: -BB_PROJECTS=openssl libssh2 nghttp2 mpfr curl libgit2 pcre libuv unwind llvmunwind dsfmt objconv p7zip zlib libsuitesparse openlibm blastrampoline libtracyclient +BB_PROJECTS=openssl libssh2 nghttp2 mpfr curl libgit2 pcre libuv unwind llvmunwind dsfmt objconv p7zip zlib libsuitesparse openlibm blastrampoline libtracyclient mmtk_julia BB_GCC_EXPANDED_PROJECTS=openblas csl BB_CXX_EXPANDED_PROJECTS=gmp llvm clang llvm-tools lld # These are non-BB source-only deps diff --git a/deps/checksums/mmtk_julia b/deps/checksums/mmtk_julia index 979ab79e52207..098937aea1991 100644 --- a/deps/checksums/mmtk_julia +++ b/deps/checksums/mmtk_julia @@ -4,3 +4,7 @@ mmtk_julia-f07d66aafc86af84ea988b35335acc9bbc770fa1.tar.gz/md5/38afb5db6d8c55413 mmtk_julia-f07d66aafc86af84ea988b35335acc9bbc770fa1.tar.gz/sha512/78525582a46a6baf8d33df7b622e55cf244439afcd7192ba55489c1bc18393d1237d2903d517c610484bf9e2a7338ad31435a9cbf70889d6bcf87c40cec829e5 mmtk_julia.v0.30.3+1.x86_64-linux-gnu.tar.gz/md5/631b204574da7062802dac501a4b711f mmtk_julia.v0.30.3+1.x86_64-linux-gnu.tar.gz/sha512/daaed59d08fc49621479ed638dea0aac0cba123986e486571447e8e21e9a098776ce2e87fbd92ddea276782fc44621f23d40fa213296b28e1d4480553c7de4f7 +mmtk_julia-c9e046baf3a0d52fe75d6c8b28f6afd69b045d95.tar.gz/md5/73a8fbea71edce30a39a30f31969dd8e +mmtk_julia-c9e046baf3a0d52fe75d6c8b28f6afd69b045d95.tar.gz/sha512/374848b7696b565dea66daa208830581f92c1fcb0138e7a7ab88564402e94bc79c54b6ed370ec68473e31e2bd411bf82c97793796c31d39aafbbfffea9c05588 +mmtk_julia.v0.30.4+0.x86_64-linux-gnu.tar.gz/md5/8cdeb14fd69945f64308be49f6912f9c +mmtk_julia.v0.30.4+0.x86_64-linux-gnu.tar.gz/sha512/3692502f65dec8c0971b56b9bf8178641892b390d520cbcd69880d75b7500e6341534d87882246e68998f590f824ec54c18f4b8fb4aa09b8f313de065c48450e diff --git a/deps/mmtk_julia.version b/deps/mmtk_julia.version index cb1e8064f9825..684197bbe3e4e 100644 --- a/deps/mmtk_julia.version +++ b/deps/mmtk_julia.version @@ -1,6 +1,6 @@ MMTK_JULIA_BRANCH = master -MMTK_JULIA_SHA1 = f07d66aafc86af84ea988b35335acc9bbc770fa1 +MMTK_JULIA_SHA1 = c9e046baf3a0d52fe75d6c8b28f6afd69b045d95 MMTK_JULIA_GIT_URL := https://github.com/mmtk/mmtk-julia.git -MMTK_JULIA_TAR_URL = https://github.com/mmtk/mmtk-julia/archive/refs/tags/v0.30.3.tar.gz -MMTK_JULIA_JLL_VER := 0.30.3+1 +MMTK_JULIA_TAR_URL = https://github.com/mmtk/mmtk-julia/archive/refs/tags/v0.30.4.tar.gz +MMTK_JULIA_JLL_VER := 0.30.4+0 MMTK_JULIA_JLL_NAME := mmtk_julia diff --git a/src/gc-mmtk.c b/src/gc-mmtk.c index 5ec1e34cc1acd..2f261a2e8e2fd 100644 --- a/src/gc-mmtk.c +++ b/src/gc-mmtk.c @@ -64,11 +64,37 @@ void jl_gc_init(void) { arraylist_new(&to_finalize, 0); arraylist_new(&finalizer_list_marked, 0); - + gc_num.interval = default_collect_interval; gc_num.allocd = 0; gc_num.max_pause = 0; gc_num.max_memory = 0; + // Necessary if we want to use Julia heap resizing heuristics + uint64_t mem_reserve = 250*1024*1024; // LLVM + other libraries need some amount of memory + uint64_t min_heap_size_hint = mem_reserve + 1*1024*1024; + uint64_t hint = jl_options.heap_size_hint; + + // check if heap size specified on command line + if (jl_options.heap_size_hint == 0) { + char *cp = getenv(HEAP_SIZE_HINT); + if (cp) + hint = parse_heap_size_hint(cp, "JULIA_HEAP_SIZE_HINT=\"[]\""); + } +#ifdef _P64 + if (hint == 0) { + uint64_t constrained_mem = uv_get_constrained_memory(); + if (constrained_mem > 0 && constrained_mem < uv_get_total_memory()) + hint = constrained_mem; + } +#endif + if (hint) { + if (hint < min_heap_size_hint) + hint = min_heap_size_hint; + jl_gc_set_max_memory(hint - mem_reserve); + } + + // MMTK supports setting the heap size using the + // MMTK_MIN_HSIZE and MMTK_MAX_HSIZE environment variables long long min_heap_size; long long max_heap_size; char* min_size_def = getenv("MMTK_MIN_HSIZE"); @@ -77,7 +103,8 @@ void jl_gc_init(void) { char* max_size_def = getenv("MMTK_MAX_HSIZE"); char* max_size_gb = getenv("MMTK_MAX_HSIZE_G"); - // default min heap currently set as Julia's default_collect_interval + // If min and max values are not specified, set them to 0 here + // and use stock heuristics as defined in the binding if (min_size_def != NULL) { char *p; double min_size = strtod(min_size_def, &p); @@ -87,10 +114,9 @@ void jl_gc_init(void) { double min_size = strtod(min_size_gb, &p); min_heap_size = (long) 1024 * 1024 * 1024 * min_size; } else { - min_heap_size = default_collect_interval; + min_heap_size = 0; } - // default max heap currently set as 70% the free memory in the system if (max_size_def != NULL) { char *p; double max_size = strtod(max_size_def, &p); @@ -100,7 +126,7 @@ void jl_gc_init(void) { double max_size = strtod(max_size_gb, &p); max_heap_size = (long) 1024 * 1024 * 1024 * max_size; } else { - max_heap_size = uv_get_free_memory() * 70 / 100; + max_heap_size = 0; } // Assert that the number of stock GC threads is 0; MMTK uses the number of threads in jl_options.ngcthreads @@ -159,7 +185,17 @@ void jl_free_thread_gc_state(struct _jl_tls_states_t *ptls) { } JL_DLLEXPORT void jl_gc_set_max_memory(uint64_t max_mem) { - // MMTk currently does not allow setting the heap size at runtime +#ifdef _P32 + max_mem = max_mem < MAX32HEAP ? max_mem : MAX32HEAP; +#endif + max_total_memory = max_mem; +} + +JL_DLLEXPORT uint64_t jl_gc_get_max_memory(void) +{ + // FIXME: We should return the max heap size set in MMTk + // when not using Julia's heap resizing heuristics + return max_total_memory; } STATIC_INLINE void maybe_collect(jl_ptls_t ptls) @@ -415,12 +451,6 @@ JL_DLLEXPORT void jl_gc_get_total_bytes(int64_t *bytes) JL_NOTSAFEPOINT *bytes = (num.total_allocd + num.deferred_alloc + num.allocd); } -JL_DLLEXPORT uint64_t jl_gc_get_max_memory(void) -{ - // FIXME: should probably return MMTk's heap size - return max_total_memory; -} - // These are needed to collect MMTk statistics from a Julia program using ccall JL_DLLEXPORT void (jl_mmtk_harness_begin)(void) { From cc3e7b67d6af2eaf4ff5d52d57a0bc63c5ec5da7 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Mon, 27 Jan 2025 13:25:59 -0500 Subject: [PATCH 30/56] Add option to use `@spawn :samepool` for using the same threadpool as the caller (#57109) --- NEWS.md | 2 ++ base/threadingconstructs.jl | 16 ++++++++++++---- test/threadpool_use.jl | 9 +++++++++ 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/NEWS.md b/NEWS.md index 71014e1e57695..3aa74034ead54 100644 --- a/NEWS.md +++ b/NEWS.md @@ -23,6 +23,8 @@ New language features - actual running time for the task (`Base.Experimental.task_running_time_ns`), and - wall-time for the task (`Base.Experimental.task_wall_time_ns`). - Support for Unicode 16 ([#56925]). +- `Threads.@spawn` now takes a `:samepool` argument to specify the same threadpool as the caller. + `Threads.@spawn :samepool foo()` which is shorthand for `Threads.@spawn Threads.threadpool() foo()` ([#57109]) Language changes ---------------- diff --git a/base/threadingconstructs.jl b/base/threadingconstructs.jl index 3d86e203ef72e..b83f47ef7c8cd 100644 --- a/base/threadingconstructs.jl +++ b/base/threadingconstructs.jl @@ -440,10 +440,11 @@ function _spawn_set_thrpool(t::Task, tp::Symbol) end """ - Threads.@spawn [:default|:interactive] expr + Threads.@spawn [:default|:interactive|:samepool] expr Create a [`Task`](@ref) and [`schedule`](@ref) it to run on any available -thread in the specified threadpool (`:default` if unspecified). The task is +thread in the specified threadpool: `:default`, `:interactive`, or `:samepool` +to use the same as the caller. `:default` is used if unspecified. The task is allocated to a thread once one becomes available. To wait for the task to finish, call [`wait`](@ref) on the result of this macro, or call [`fetch`](@ref) to wait and then obtain its return value. @@ -468,6 +469,9 @@ the variable's value in the current task. !!! compat "Julia 1.9" A threadpool may be specified as of Julia 1.9. +!!! compat "Julia 1.12" + The same threadpool may be specified as of Julia 1.12. + # Examples ```julia-repl julia> t() = println("Hello from ", Threads.threadid()); @@ -486,7 +490,7 @@ macro spawn(args...) ttype, ex = args if ttype isa QuoteNode ttype = ttype.value - if ttype !== :interactive && ttype !== :default + if !in(ttype, (:interactive, :default, :samepool)) throw(ArgumentError(LazyString("unsupported threadpool in @spawn: ", ttype))) end tp = QuoteNode(ttype) @@ -507,7 +511,11 @@ macro spawn(args...) let $(letargs...) local task = Task($thunk) task.sticky = false - _spawn_set_thrpool(task, $(esc(tp))) + local tp = $(esc(tp)) + if tp == :samepool + tp = Threads.threadpool() + end + _spawn_set_thrpool(task, tp) if $(Expr(:islocal, var)) put!($var, task) end diff --git a/test/threadpool_use.jl b/test/threadpool_use.jl index 7523991fdf6a7..e76d50c7a3fd1 100644 --- a/test/threadpool_use.jl +++ b/test/threadpool_use.jl @@ -9,6 +9,15 @@ using Base.Threads @test fetch(Threads.@spawn Threads.threadpool()) === :default @test fetch(Threads.@spawn :default Threads.threadpool()) === :default @test fetch(Threads.@spawn :interactive Threads.threadpool()) === :interactive +@test fetch(Threads.@spawn :samepool Threads.threadpool()) === Threads.threadpool() +@sync for tp in [:interactive, :default] + Threads.@spawn tp begin + @test fetch(Threads.@spawn :samepool Threads.threadpool()) === Threads.threadpool() + end +end +wait(Threads.@spawn :interactive begin + @test fetch(Threads.@spawn :samepool Threads.threadpool()) === Threads.threadpool() +end) tp = :default @test fetch(Threads.@spawn tp Threads.threadpool()) === :default tp = :interactive From c19a6bb4c2512a33ee6cee52aa2e4175227cecb8 Mon Sep 17 00:00:00 2001 From: DilumAluthgeBot <43731525+DilumAluthgeBot@users.noreply.github.com> Date: Mon, 27 Jan 2025 14:56:43 -0500 Subject: [PATCH 31/56] =?UTF-8?q?=F0=9F=A4=96=20[master]=20Bump=20the=20Di?= =?UTF-8?q?stributed=20stdlib=20from=20c613685=20to=208890288=20(#57169)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: IanButterworth <1694067+IanButterworth@users.noreply.github.com> --- .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 - .../sha512 | 1 - stdlib/Distributed.version | 2 +- 5 files changed, 3 insertions(+), 3 deletions(-) create mode 100644 deps/checksums/Distributed-8890288f01a3b4c2b64c87d98409bc9d865f506e.tar.gz/md5 create mode 100644 deps/checksums/Distributed-8890288f01a3b4c2b64c87d98409bc9d865f506e.tar.gz/sha512 delete mode 100644 deps/checksums/Distributed-c6136853451677f1957bec20ecce13419cde3a12.tar.gz/md5 delete mode 100644 deps/checksums/Distributed-c6136853451677f1957bec20ecce13419cde3a12.tar.gz/sha512 diff --git a/deps/checksums/Distributed-8890288f01a3b4c2b64c87d98409bc9d865f506e.tar.gz/md5 b/deps/checksums/Distributed-8890288f01a3b4c2b64c87d98409bc9d865f506e.tar.gz/md5 new file mode 100644 index 0000000000000..3b640d12f3344 --- /dev/null +++ b/deps/checksums/Distributed-8890288f01a3b4c2b64c87d98409bc9d865f506e.tar.gz/md5 @@ -0,0 +1 @@ +7405afe10033da0431c8fd920a8cbbbf diff --git a/deps/checksums/Distributed-8890288f01a3b4c2b64c87d98409bc9d865f506e.tar.gz/sha512 b/deps/checksums/Distributed-8890288f01a3b4c2b64c87d98409bc9d865f506e.tar.gz/sha512 new file mode 100644 index 0000000000000..e9003e31edcba --- /dev/null +++ b/deps/checksums/Distributed-8890288f01a3b4c2b64c87d98409bc9d865f506e.tar.gz/sha512 @@ -0,0 +1 @@ +ad3498cfee95bcd088e47c15eb2707f47ced9493881ec356cbeb22f66207406d23a3e3b27e70a00be7c2c755c6651f54f5378ef42bf4d1312c84d589010aab7b diff --git a/deps/checksums/Distributed-c6136853451677f1957bec20ecce13419cde3a12.tar.gz/md5 b/deps/checksums/Distributed-c6136853451677f1957bec20ecce13419cde3a12.tar.gz/md5 deleted file mode 100644 index e1c0f9e87b7c7..0000000000000 --- a/deps/checksums/Distributed-c6136853451677f1957bec20ecce13419cde3a12.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -98b8b8bc0ea4bf24c4b2986a5b7ae3e9 diff --git a/deps/checksums/Distributed-c6136853451677f1957bec20ecce13419cde3a12.tar.gz/sha512 b/deps/checksums/Distributed-c6136853451677f1957bec20ecce13419cde3a12.tar.gz/sha512 deleted file mode 100644 index ed816ebc21e97..0000000000000 --- a/deps/checksums/Distributed-c6136853451677f1957bec20ecce13419cde3a12.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -4043933825bf716f2733f8e90632de34a95a437f3b31cda92edd510ffee208f8e374ec3c5922c8142342ae21b4ec4cbd1ecd4036b9057056a12c86169632ac7b diff --git a/stdlib/Distributed.version b/stdlib/Distributed.version index 4a7ab49defed2..13d49b7a093de 100644 --- a/stdlib/Distributed.version +++ b/stdlib/Distributed.version @@ -1,4 +1,4 @@ DISTRIBUTED_BRANCH = master -DISTRIBUTED_SHA1 = c6136853451677f1957bec20ecce13419cde3a12 +DISTRIBUTED_SHA1 = 8890288f01a3b4c2b64c87d98409bc9d865f506e DISTRIBUTED_GIT_URL := https://github.com/JuliaLang/Distributed.jl DISTRIBUTED_TAR_URL = https://api.github.com/repos/JuliaLang/Distributed.jl/tarball/$1 From 779b750e858effcdafe0b39357493d22de2fa404 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Mon, 27 Jan 2025 19:06:12 -0500 Subject: [PATCH 32/56] Use faster timeout on cmdlineargs test that relies on timeout (#57164) --- test/cmdlineargs.jl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test/cmdlineargs.jl b/test/cmdlineargs.jl index ec7a51e804d3f..efcdf52b68093 100644 --- a/test/cmdlineargs.jl +++ b/test/cmdlineargs.jl @@ -798,7 +798,10 @@ let exename = `$(Base.julia_cmd()) --startup-file=no --color=no` # --worker takes default / custom as argument (default/custom arguments # tested in test/parallel.jl) - @test errors_not_signals(`$exename --worker=true`) + # shorten the worker timeout as this test relies on it timing out + withenv("JULIA_WORKER_TIMEOUT" => "10") do + @test errors_not_signals(`$exename --worker=true`) + end # --trace-compile let From d5523b6698e88d540486098a336ec0de9934b138 Mon Sep 17 00:00:00 2001 From: Cody Tapscott <84105208+topolarity@users.noreply.github.com> Date: Mon, 27 Jan 2025 20:14:47 -0500 Subject: [PATCH 33/56] typeinfer: Enqueue CIs for `ccallable` entrypoints (#57158) This is another step in the long-term direction to have type-inference fully-explore the code graph we want to emit, largely side-stepping the vestigial `jl_type_infer` paths that we still have in type-inference for ccallable (and OpaqueClosure) This is needed for `juliac`, since the trim checks in codegen check if code was enqueued in the workqueue as expected. --------- Co-authored-by: Jameson Nash --- Compiler/src/typeinfer.jl | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/Compiler/src/typeinfer.jl b/Compiler/src/typeinfer.jl index e3896870d82b8..dd6a55bfc9f1c 100644 --- a/Compiler/src/typeinfer.jl +++ b/Compiler/src/typeinfer.jl @@ -1280,8 +1280,20 @@ function typeinf_ext_toplevel(methods::Vector{Any}, worlds::Vector{UInt}, trim:: ci isa CodeInstance && !use_const_api(ci) && push!(tocompile, ci) end elseif item isa SimpleVector - push!(codeinfos, item[1]::Type) - push!(codeinfos, item[2]::Type) + (rt::Type, sig::Type) = item + # make a best-effort attempt to enqueue the relevant code for the ccallable + ptr = ccall(:jl_get_specialization1, + #= MethodInstance =# Ptr{Cvoid}, (Any, Csize_t, Cint), + sig, this_world, #= mt_cache =# 0) + if ptr !== C_NULL + mi = unsafe_pointer_to_objref(ptr) + ci = typeinf_ext(interp, mi, SOURCE_MODE_NOT_REQUIRED) + ci isa CodeInstance && !use_const_api(ci) && push!(tocompile, ci) + end + # additionally enqueue the ccallable entrypoint / adapter, which implicitly + # invokes the above ci + push!(codeinfos, rt) + push!(codeinfos, sig) end end while !isempty(tocompile) From fbe865657942da7d73cc02f76064f9ba9cdef56c Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Mon, 27 Jan 2025 22:43:54 -0500 Subject: [PATCH 34/56] Default to 1 interactive thread (#57087) --- NEWS.md | 7 ++ base/channels.jl | 3 +- doc/src/manual/faq.md | 6 +- doc/src/manual/methods.md | 4 +- doc/src/manual/multi-threading.md | 24 +++++- src/jloptions.c | 25 +++--- src/threading.c | 13 +-- stdlib/REPL/test/replcompletions.jl | 4 +- test/gcext/gcext-test.jl | 2 +- test/gcext/gcext.c | 7 ++ test/threads.jl | 21 ++++- test/threads_exec.jl | 122 ++++++++++++++-------------- 12 files changed, 143 insertions(+), 95 deletions(-) diff --git a/NEWS.md b/NEWS.md index 3aa74034ead54..796fe9647fff0 100644 --- a/NEWS.md +++ b/NEWS.md @@ -29,6 +29,13 @@ New language features Language changes ---------------- + - Julia now defaults to 1 "interactive" thread, in addition to the 1 "default" worker thread. i.e. `-t1,1` + This means in default configuration the main task and repl (when in interactive mode), which both run on + thread 1, now run within the `interactive` threadpool. Also the libuv IO loop runs on thread 1, + helping efficient utilization of the "default" worker threadpool, which is what `Threads.@threads` and a bare + `Threads.@spawn` uses. Use `0` to disable the interactive thread i.e. `-t1,0` or `JULIA_NUM_THREADS=1,0`, or + `-tauto,0` etc. The zero is explicitly required to disable it, `-t2` will set the equivalent of `-t2,1` ([#57087]) + - When methods are replaced with exactly equivalent ones, the old method is no longer deleted implicitly simultaneously, although the new method does take priority and become more specific than the old method. Thus if the new diff --git a/base/channels.jl b/base/channels.jl index 527c22b3d45fd..ef508bd40e3ed 100644 --- a/base/channels.jl +++ b/base/channels.jl @@ -130,8 +130,7 @@ julia> chnl = Channel{Char}(1, spawn=true) do ch for c in "hello world" put!(ch, c) end - end -Channel{Char}(1) (2 items available) + end; julia> String(collect(chnl)) "hello world" diff --git a/doc/src/manual/faq.md b/doc/src/manual/faq.md index 2673ca7532acf..178a674e46643 100644 --- a/doc/src/manual/faq.md +++ b/doc/src/manual/faq.md @@ -941,7 +941,7 @@ While the streaming I/O API is synchronous, the underlying implementation is ful Consider the printed output from the following: -```jldoctest +``` julia> @sync for i in 1:3 Threads.@spawn write(stdout, string(i), " Foo ", " Bar ") end @@ -954,7 +954,7 @@ yields to other tasks while waiting for that part of the I/O to complete. `print` and `println` "lock" the stream during a call. Consequently changing `write` to `println` in the above example results in: -```jldoctest +``` julia> @sync for i in 1:3 Threads.@spawn println(stdout, string(i), " Foo ", " Bar ") end @@ -965,7 +965,7 @@ julia> @sync for i in 1:3 You can lock your writes with a `ReentrantLock` like this: -```jldoctest +``` julia> l = ReentrantLock(); julia> @sync for i in 1:3 diff --git a/doc/src/manual/methods.md b/doc/src/manual/methods.md index e448c62465b0d..45d22e08aaffe 100644 --- a/doc/src/manual/methods.md +++ b/doc/src/manual/methods.md @@ -615,7 +615,7 @@ Start some other operations that use `f(x)`: julia> g(x) = f(x) g (generic function with 1 method) -julia> t = Threads.@spawn f(wait()); yield(); +julia> t = @async f(wait()); yield(); ``` Now we add some new methods to `f(x)`: @@ -640,7 +640,7 @@ julia> g(1) julia> fetch(schedule(t, 1)) "original definition" -julia> t = Threads.@spawn f(wait()); yield(); +julia> t = @async f(wait()); yield(); julia> fetch(schedule(t, 1)) "definition for Int" diff --git a/doc/src/manual/multi-threading.md b/doc/src/manual/multi-threading.md index 209e2ffe1da56..ec470f867cc47 100644 --- a/doc/src/manual/multi-threading.md +++ b/doc/src/manual/multi-threading.md @@ -5,11 +5,13 @@ of Julia multi-threading features. ## Starting Julia with multiple threads -By default, Julia starts up with a single thread of execution. This can be verified by using the -command [`Threads.nthreads()`](@ref): +By default, Julia starts up with 2 threads of execution; 1 worker thread and 1 interactive thread. +This can be verified by using the command [`Threads.nthreads()`](@ref): ```jldoctest -julia> Threads.nthreads() +julia> Threads.nthreads(:default) +1 +julia> Threads.nthreads(:interactive) 1 ``` @@ -22,6 +24,9 @@ The number of threads can either be specified as an integer (`--threads=4`) or a (`--threads=auto`), where `auto` tries to infer a useful default number of threads to use (see [Command-line Options](@ref command-line-interface) for more details). +See [threadpools](@ref man-threadpools) for how to control how many `:default` and `:interactive` threads are in +each threadpool. + !!! compat "Julia 1.5" The `-t`/`--threads` command line argument requires at least Julia 1.5. In older versions you must use the environment variable instead. @@ -29,6 +34,10 @@ The number of threads can either be specified as an integer (`--threads=4`) or a !!! compat "Julia 1.7" Using `auto` as value of the environment variable [`JULIA_NUM_THREADS`](@ref JULIA_NUM_THREADS) requires at least Julia 1.7. In older versions, this value is ignored. + +!!! compat "Julia 1.12" + Starting by default with 1 interactive thread, as well as the 1 worker thread, was made as such in Julia 1.12 + Lets start Julia with 4 threads: ```bash @@ -96,10 +105,17 @@ using Base.Threads Interactive tasks should avoid performing high latency operations, and if they are long duration tasks, should yield frequently. -Julia may be started with one or more threads reserved to run interactive tasks: +By default Julia starts with one interactive thread reserved to run interactive tasks, but that number can +be controlled with: ```bash $ julia --threads 3,1 +julia> Threads.nthreads(:interactive) +1 + +$ julia --threads 3,0 +julia> Threads.nthreads(:interactive) +0 ``` The environment variable [`JULIA_NUM_THREADS`](@ref JULIA_NUM_THREADS) can also be used similarly: diff --git a/src/jloptions.c b/src/jloptions.c index 2c5a9074eb465..ac515bea19845 100644 --- a/src/jloptions.c +++ b/src/jloptions.c @@ -623,8 +623,13 @@ JL_DLLEXPORT void jl_parse_opts(int *argcp, char ***argvp) break; case 't': // threads errno = 0; - jl_options.nthreadpools = 1; - long nthreads = -1, nthreadsi = 0; + jl_options.nthreadpools = 2; + // By default: + // default threads = -1 (== "auto") + long nthreads = -1; + // interactive threads = 1, or 0 if generating output + long nthreadsi = jl_generating_output() ? 0 : 1; + if (!strncmp(optarg, "auto", 4)) { jl_options.nthreads = -1; if (optarg[4] == ',') { @@ -633,10 +638,9 @@ JL_DLLEXPORT void jl_parse_opts(int *argcp, char ***argvp) else { errno = 0; nthreadsi = strtol(&optarg[5], &endptr, 10); - if (errno != 0 || endptr == &optarg[5] || *endptr != 0 || nthreadsi < 1 || nthreadsi >= INT16_MAX) - jl_errorf("julia: -t,--threads=auto,; m must be an integer >= 1"); + if (errno != 0 || endptr == &optarg[5] || *endptr != 0 || nthreadsi < 0 || nthreadsi >= INT16_MAX) + jl_errorf("julia: -t,--threads=auto,; m must be an integer >= 0"); } - jl_options.nthreadpools++; } } else { @@ -650,17 +654,18 @@ JL_DLLEXPORT void jl_parse_opts(int *argcp, char ***argvp) errno = 0; char *endptri; nthreadsi = strtol(&endptr[1], &endptri, 10); - if (errno != 0 || endptri == &endptr[1] || *endptri != 0 || nthreadsi < 1 || nthreadsi >= INT16_MAX) - jl_errorf("julia: -t,--threads=,; n and m must be integers >= 1"); + // Allow 0 for interactive + if (errno != 0 || endptri == &endptr[1] || *endptri != 0 || nthreadsi < 0 || nthreadsi >= INT16_MAX) + jl_errorf("julia: -t,--threads=,; m must be an integer ≥ 0"); + if (nthreadsi == 0) + jl_options.nthreadpools = 1; } - jl_options.nthreadpools++; } jl_options.nthreads = nthreads + nthreadsi; } int16_t *ntpp = (int16_t *)malloc_s(jl_options.nthreadpools * sizeof(int16_t)); ntpp[0] = (int16_t)nthreads; - if (jl_options.nthreadpools == 2) - ntpp[1] = (int16_t)nthreadsi; + ntpp[1] = (int16_t)nthreadsi; jl_options.nthreads_per_pool = ntpp; break; case 'p': // procs diff --git a/src/threading.c b/src/threading.c index ab63114fc9c8f..a51916cdcd8d8 100644 --- a/src/threading.c +++ b/src/threading.c @@ -698,15 +698,15 @@ void jl_init_threading(void) // and `jl_n_threads_per_pool`. jl_n_threadpools = 2; int16_t nthreads = JULIA_NUM_THREADS; - int16_t nthreadsi = 0; + // if generating output default to 0 interactive threads, otherwise default to 1 + int16_t nthreadsi = jl_generating_output() ? 0 : 1; char *endptr, *endptri; if (jl_options.nthreads != 0) { // --threads specified nthreads = jl_options.nthreads_per_pool[0]; if (nthreads < 0) nthreads = jl_effective_threads(); - if (jl_options.nthreadpools == 2) - nthreadsi = jl_options.nthreads_per_pool[1]; + nthreadsi = (jl_options.nthreadpools == 1) ? 0 : jl_options.nthreads_per_pool[1]; } else if ((cp = getenv(NUM_THREADS_NAME))) { // ENV[NUM_THREADS_NAME] specified if (!strncmp(cp, "auto", 4)) { @@ -722,13 +722,16 @@ void jl_init_threading(void) } if (*cp == ',') { cp++; - if (!strncmp(cp, "auto", 4)) + if (!strncmp(cp, "auto", 4)) { nthreadsi = 1; + cp += 4; + } else { errno = 0; nthreadsi = strtol(cp, &endptri, 10); if (errno != 0 || endptri == cp || nthreadsi < 0) - nthreadsi = 0; + nthreadsi = 1; + cp = endptri; } } } diff --git a/stdlib/REPL/test/replcompletions.jl b/stdlib/REPL/test/replcompletions.jl index 2c8d48cc232cf..77d056b63655d 100644 --- a/stdlib/REPL/test/replcompletions.jl +++ b/stdlib/REPL/test/replcompletions.jl @@ -1187,7 +1187,7 @@ let s, c, r REPL.REPLCompletions.next_cache_update = 0 end c,r = test_scomplete(s) - wait(REPL.REPLCompletions.PATH_cache_task::Task) # wait for caching to complete + timedwait(()->REPL.REPLCompletions.next_cache_update != 0, 5) # wait for caching to complete c,r = test_scomplete(s) @test "tmp-executable" in c @test r == 1:9 @@ -1221,7 +1221,7 @@ let s, c, r REPL.REPLCompletions.next_cache_update = 0 end c,r = test_scomplete(s) - wait(REPL.REPLCompletions.PATH_cache_task::Task) # wait for caching to complete + timedwait(()->REPL.REPLCompletions.next_cache_update != 0, 5) # wait for caching to complete c,r = test_scomplete(s) @test ["repl-completion"] == c @test s[r] == "repl-completio" diff --git a/test/gcext/gcext-test.jl b/test/gcext/gcext-test.jl index 81637392e3c5d..86b6ad1a2ce59 100644 --- a/test/gcext/gcext-test.jl +++ b/test/gcext/gcext-test.jl @@ -31,7 +31,7 @@ end # @test success(p) errlines = fetch(err_task) lines = fetch(out_task) - @test length(errlines) == 0 + @test isempty(errlines) # @test length(lines) == 6 @test length(lines) == 5 @test checknum(lines[2], r"([0-9]+) full collections", n -> n >= 10) diff --git a/test/gcext/gcext.c b/test/gcext/gcext.c index d5bf91ec8c9ab..3da44a388a5b8 100644 --- a/test/gcext/gcext.c +++ b/test/gcext/gcext.c @@ -600,6 +600,13 @@ int main() jl_gc_set_cb_notify_external_alloc(alloc_bigval, 1); jl_gc_set_cb_notify_external_free(free_bigval, 1); + // single threaded mode + // Note: with -t1,1 a signal 10 occurs in task_scanner + jl_options.nthreadpools = 1; + jl_options.nthreads = 1; + int16_t ntpp[] = {jl_options.nthreads}; + jl_options.nthreads_per_pool = ntpp; + jl_init(); if (jl_gc_enable_conservative_gc_support() < 0) abort(); diff --git a/test/threads.jl b/test/threads.jl index 43fbea1e351a2..a1da408252fc4 100644 --- a/test/threads.jl +++ b/test/threads.jl @@ -72,11 +72,24 @@ let e = Event(true), started1 = Event(true), started2 = Event(true), done = Even end end -let cmd = `$(Base.julia_cmd()) --depwarn=error --rr-detach --startup-file=no threads_exec.jl` - for test_nthreads in (1, 2, 4, 4) # run once to try single-threaded mode, then try a couple times to trigger bad races + +let cmd1 = `$(Base.julia_cmd()) --depwarn=error --rr-detach --startup-file=no threads_exec.jl`, + cmd2 = `$(Base.julia_cmd()) --depwarn=error --rr-detach --startup-file=no -e 'print(Threads.threadpoolsize(:default), ",", Threads.threadpoolsize(:interactive))'` + for (test_nthreads, test_nthreadsi) in ( + (1, 0), + (1, 1), + (2, 0), + (2, 1), + (4, 0), + (4, 0)) # try a couple times to trigger bad races new_env = copy(ENV) - new_env["JULIA_NUM_THREADS"] = string(test_nthreads) - run(pipeline(setenv(cmd, new_env), stdout = stdout, stderr = stderr)) + new_env["JULIA_NUM_THREADS"] = string(test_nthreads, ",", test_nthreadsi) + run(pipeline(setenv(cmd1, new_env), stdout = stdout, stderr = stderr)) + threads_config = "$test_nthreads,$test_nthreadsi" + # threads set via env var + @test chomp(read(setenv(cmd2, new_env), String)) == threads_config + # threads set via -t + @test chomp(read(`$cmd2 -t$test_nthreads,$test_nthreadsi`, String)) == threads_config end end diff --git a/test/threads_exec.jl b/test/threads_exec.jl index 65e5ef57c911b..7091893908cb3 100644 --- a/test/threads_exec.jl +++ b/test/threads_exec.jl @@ -31,10 +31,11 @@ Timer(t -> killjob("KILLING BY THREAD TEST WATCHDOG\n"), 1200) @testset """threads_exec.jl with JULIA_NUM_THREADS == $(ENV["JULIA_NUM_THREADS"])""" begin @test Threads.threadid() == 1 -@test 1 <= threadpoolsize() <= Threads.maxthreadid() +@test threadpool() in (:interactive, :default) # thread 1 could be in the interactive pool +@test 1 <= threadpoolsize(:default) <= Threads.maxthreadid() # basic lock check -if threadpoolsize() > 1 +if threadpoolsize(:default) > 1 let lk = SpinLock() c1 = Base.Event() c2 = Base.Event() @@ -56,7 +57,18 @@ end # threading constructs -let a = zeros(Int, 2 * threadpoolsize()) +@testset "@threads and @spawn threadpools" begin + @threads for i in 1:1 + @test threadpool() == :default + end + @test fetch(Threads.@spawn threadpool()) == :default + @test fetch(Threads.@spawn :default threadpool()) == :default + if threadpoolsize(:interactive) > 0 + @test fetch(Threads.@spawn :interactive threadpool()) == :interactive + end +end + +let a = zeros(Int, 2 * threadpoolsize(:default)) @threads for i = 1:length(a) @sync begin @async begin @@ -76,7 +88,7 @@ end # parallel loop with parallel atomic addition function threaded_loop(a, r, x) - counter = Threads.Atomic{Int}(min(threadpoolsize(), length(r))) + counter = Threads.Atomic{Int}(min(threadpoolsize(:default), length(r))) @threads for i in r # synchronize the start given that each partition is started sequentially, # meaning that without the wait, if the loop is too fast the iteration can happen in order @@ -429,7 +441,7 @@ end for T in intersect((Int32, Int64, Float32, Float64), Base.Threads.atomictypes) var = Atomic{T}() nloops = 1000 - di = threadpoolsize() + di = threadpoolsize(:default) @threads for i in 1:di test_atomic_cas!(var, i:di:nloops) end @@ -519,13 +531,13 @@ function test_thread_cfunction() @test cfs[1] == cf1 @test cfs[2] == cf(fs[2]) @test length(unique(cfs)) == 1000 - ok = zeros(Int, threadpoolsize()) + ok = zeros(Int, threadpoolsize(:default)) @threads :static for i in 1:10000 i = mod1(i, 1000) fi = fs[i] cfi = cf(fi) GC.@preserve cfi begin - ok[threadid()] += (cfi === cfs[i]) + ok[threadid() - threadpoolsize(:interactive)] += (cfi === cfs[i]) end end @test sum(ok) == 10000 @@ -534,20 +546,6 @@ if cfunction_closure test_thread_cfunction() end -function test_thread_range() - a = zeros(Int, threadpoolsize()) - @threads for i in 1:threadid() - a[i] = 1 - end - for i in 1:threadid() - @test a[i] == 1 - end - for i in (threadid() + 1):threadpoolsize() - @test a[i] == 0 - end -end -test_thread_range() - # Thread safety of `jl_load_and_lookup`. function test_load_and_lookup_18020(n) @threads for i in 1:n @@ -582,17 +580,17 @@ test_nested_loops() function test_thread_too_few_iters() x = Atomic() - a = zeros(Int, threadpoolsize()+2) - threaded_loop(a, 1:threadpoolsize()-1, x) - found = zeros(Bool, threadpoolsize()+2) - for i=1:threadpoolsize()-1 + a = zeros(Int, threadpoolsize(:default)+2) + threaded_loop(a, 1:threadpoolsize(:default)-1, x) + found = zeros(Bool, threadpoolsize(:default)+2) + for i=1:threadpoolsize(:default)-1 found[a[i]] = true end - @test x[] == threadpoolsize()-1 + @test x[] == threadpoolsize(:default)-1 # Next test checks that all loop iterations ran, # and were unique (via pigeon-hole principle). - @test !(false in found[1:threadpoolsize()-1]) - @test !(true in found[threadpoolsize():end]) + @test !(false in found[1:threadpoolsize(:default)-1]) + @test !(true in found[threadpoolsize(:default):end]) end test_thread_too_few_iters() @@ -734,10 +732,10 @@ function _atthreads_with_error(a, err) end a end -@test_throws CompositeException _atthreads_with_error(zeros(threadpoolsize()), true) -let a = zeros(threadpoolsize()) +@test_throws CompositeException _atthreads_with_error(zeros(threadpoolsize(:default)), true) +let a = zeros(threadpoolsize(:default)) _atthreads_with_error(a, false) - @test a == [1:threadpoolsize();] + @test a == [threadpoolsize(:interactive) .+ (1:threadpoolsize(:default));] end # static schedule @@ -748,11 +746,11 @@ function _atthreads_static_schedule(n) end return ids end -@test _atthreads_static_schedule(threadpoolsize()) == 1:threadpoolsize() -@test _atthreads_static_schedule(1) == [1;] +@test _atthreads_static_schedule(threadpoolsize(:default)) == threadpoolsize(:interactive) .+ (1:threadpoolsize(:default)) +@test _atthreads_static_schedule(1) == [threadpoolsize(:interactive) + 1;] @test_throws( "`@threads :static` cannot be used concurrently or nested", - @threads(for i = 1:1; _atthreads_static_schedule(threadpoolsize()); end), + @threads(for i = 1:1; _atthreads_static_schedule(threadpoolsize(:default)); end), ) # dynamic schedule @@ -765,35 +763,35 @@ function _atthreads_dynamic_schedule(n) end return inc[], flags end -@test _atthreads_dynamic_schedule(threadpoolsize()) == (threadpoolsize(), ones(threadpoolsize())) +@test _atthreads_dynamic_schedule(threadpoolsize(:default)) == (threadpoolsize(:default), ones(threadpoolsize(:default))) @test _atthreads_dynamic_schedule(1) == (1, ones(1)) @test _atthreads_dynamic_schedule(10) == (10, ones(10)) -@test _atthreads_dynamic_schedule(threadpoolsize() * 2) == (threadpoolsize() * 2, ones(threadpoolsize() * 2)) +@test _atthreads_dynamic_schedule(threadpoolsize(:default) * 2) == (threadpoolsize(:default) * 2, ones(threadpoolsize(:default) * 2)) # nested dynamic schedule function _atthreads_dynamic_dynamic_schedule() inc = Threads.Atomic{Int}(0) - Threads.@threads :dynamic for _ = 1:threadpoolsize() - Threads.@threads :dynamic for _ = 1:threadpoolsize() + Threads.@threads :dynamic for _ = 1:threadpoolsize(:default) + Threads.@threads :dynamic for _ = 1:threadpoolsize(:default) Threads.atomic_add!(inc, 1) end end return inc[] end -@test _atthreads_dynamic_dynamic_schedule() == threadpoolsize() * threadpoolsize() +@test _atthreads_dynamic_dynamic_schedule() == threadpoolsize(:default) * threadpoolsize(:default) function _atthreads_static_dynamic_schedule() - ids = zeros(Int, threadpoolsize()) + ids = zeros(Int, threadpoolsize(:default)) inc = Threads.Atomic{Int}(0) - Threads.@threads :static for i = 1:threadpoolsize() + Threads.@threads :static for i = 1:threadpoolsize(:default) ids[i] = Threads.threadid() - Threads.@threads :dynamic for _ = 1:threadpoolsize() + Threads.@threads :dynamic for _ = 1:threadpoolsize(:default) Threads.atomic_add!(inc, 1) end end return ids, inc[] end -@test _atthreads_static_dynamic_schedule() == (1:threadpoolsize(), threadpoolsize() * threadpoolsize()) +@test _atthreads_static_dynamic_schedule() == (threadpoolsize(:interactive) .+ (1:threadpoolsize(:default)), threadpoolsize(:default) * threadpoolsize(:default)) # errors inside @threads :dynamic function _atthreads_dynamic_with_error(a) @@ -802,7 +800,7 @@ function _atthreads_dynamic_with_error(a) end a end -@test_throws "user error in the loop body" _atthreads_dynamic_with_error(zeros(threadpoolsize())) +@test_throws "user error in the loop body" _atthreads_dynamic_with_error(zeros(threadpoolsize(:default))) #### # :greedy @@ -817,57 +815,57 @@ function _atthreads_greedy_schedule(n) end return inc[], flags end -@test _atthreads_greedy_schedule(threadpoolsize()) == (threadpoolsize(), ones(threadpoolsize())) +@test _atthreads_greedy_schedule(threadpoolsize(:default)) == (threadpoolsize(:default), ones(threadpoolsize(:default))) @test _atthreads_greedy_schedule(1) == (1, ones(1)) @test _atthreads_greedy_schedule(10) == (10, ones(10)) -@test _atthreads_greedy_schedule(threadpoolsize() * 2) == (threadpoolsize() * 2, ones(threadpoolsize() * 2)) +@test _atthreads_greedy_schedule(threadpoolsize(:default) * 2) == (threadpoolsize(:default) * 2, ones(threadpoolsize(:default) * 2)) # nested greedy schedule function _atthreads_greedy_greedy_schedule() inc = Threads.Atomic{Int}(0) - Threads.@threads :greedy for _ = 1:threadpoolsize() - Threads.@threads :greedy for _ = 1:threadpoolsize() + Threads.@threads :greedy for _ = 1:threadpoolsize(:default) + Threads.@threads :greedy for _ = 1:threadpoolsize(:default) Threads.atomic_add!(inc, 1) end end return inc[] end -@test _atthreads_greedy_greedy_schedule() == threadpoolsize() * threadpoolsize() +@test _atthreads_greedy_greedy_schedule() == threadpoolsize(:default) * threadpoolsize(:default) function _atthreads_greedy_dynamic_schedule() inc = Threads.Atomic{Int}(0) - Threads.@threads :greedy for _ = 1:threadpoolsize() - Threads.@threads :dynamic for _ = 1:threadpoolsize() + Threads.@threads :greedy for _ = 1:threadpoolsize(:default) + Threads.@threads :dynamic for _ = 1:threadpoolsize(:default) Threads.atomic_add!(inc, 1) end end return inc[] end -@test _atthreads_greedy_dynamic_schedule() == threadpoolsize() * threadpoolsize() +@test _atthreads_greedy_dynamic_schedule() == threadpoolsize(:default) * threadpoolsize(:default) function _atthreads_dymamic_greedy_schedule() inc = Threads.Atomic{Int}(0) - Threads.@threads :dynamic for _ = 1:threadpoolsize() - Threads.@threads :greedy for _ = 1:threadpoolsize() + Threads.@threads :dynamic for _ = 1:threadpoolsize(:default) + Threads.@threads :greedy for _ = 1:threadpoolsize(:default) Threads.atomic_add!(inc, 1) end end return inc[] end -@test _atthreads_dymamic_greedy_schedule() == threadpoolsize() * threadpoolsize() +@test _atthreads_dymamic_greedy_schedule() == threadpoolsize(:default) * threadpoolsize(:default) function _atthreads_static_greedy_schedule() - ids = zeros(Int, threadpoolsize()) + ids = zeros(Int, threadpoolsize(:default)) inc = Threads.Atomic{Int}(0) - Threads.@threads :static for i = 1:threadpoolsize() + Threads.@threads :static for i = 1:threadpoolsize(:default) ids[i] = Threads.threadid() - Threads.@threads :greedy for _ = 1:threadpoolsize() + Threads.@threads :greedy for _ = 1:threadpoolsize(:default) Threads.atomic_add!(inc, 1) end end return ids, inc[] end -@test _atthreads_static_greedy_schedule() == (1:threadpoolsize(), threadpoolsize() * threadpoolsize()) +@test _atthreads_static_greedy_schedule() == (threadpoolsize(:interactive) .+ (1:threadpoolsize(:default)), threadpoolsize(:default) * threadpoolsize(:default)) # errors inside @threads :greedy function _atthreads_greedy_with_error(a) @@ -876,7 +874,7 @@ function _atthreads_greedy_with_error(a) end a end -@test_throws "user error in the loop body" _atthreads_greedy_with_error(zeros(threadpoolsize())) +@test_throws "user error in the loop body" _atthreads_greedy_with_error(zeros(threadpoolsize(:default))) #### # multi-argument loop @@ -1109,7 +1107,7 @@ function check_sync_end_race() nnotscheduled += y === :notscheduled end # Useful for tuning the test: - @debug "`check_sync_end_race` done" threadpoolsize() ncompleted nnotscheduled nerror + @debug "`check_sync_end_race` done" threadpoolsize(:default) ncompleted nnotscheduled nerror finally done[] = true end @@ -1123,7 +1121,7 @@ end # issue #41546, thread-safe package loading @testset "package loading" begin - ntasks = max(threadpoolsize(), 4) + ntasks = max(threadpoolsize(:default), 4) ch = Channel{Bool}(ntasks) barrier = Base.Event() old_act_proj = Base.ACTIVE_PROJECT[] From eff8ba4c18022896321a8062cd1eb476f4221375 Mon Sep 17 00:00:00 2001 From: DilumAluthgeBot <43731525+DilumAluthgeBot@users.noreply.github.com> Date: Tue, 28 Jan 2025 00:46:23 -0500 Subject: [PATCH 35/56] =?UTF-8?q?=F0=9F=A4=96=20[master]=20Bump=20the=20Di?= =?UTF-8?q?stributed=20stdlib=20from=208890288=20to=2051e5297=20(#57175)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 - .../sha512 | 1 - stdlib/Distributed.version | 2 +- 5 files changed, 3 insertions(+), 3 deletions(-) create mode 100644 deps/checksums/Distributed-51e52978481835413d15b589919aba80dd85f890.tar.gz/md5 create mode 100644 deps/checksums/Distributed-51e52978481835413d15b589919aba80dd85f890.tar.gz/sha512 delete mode 100644 deps/checksums/Distributed-8890288f01a3b4c2b64c87d98409bc9d865f506e.tar.gz/md5 delete mode 100644 deps/checksums/Distributed-8890288f01a3b4c2b64c87d98409bc9d865f506e.tar.gz/sha512 diff --git a/deps/checksums/Distributed-51e52978481835413d15b589919aba80dd85f890.tar.gz/md5 b/deps/checksums/Distributed-51e52978481835413d15b589919aba80dd85f890.tar.gz/md5 new file mode 100644 index 0000000000000..cdf885890db7c --- /dev/null +++ b/deps/checksums/Distributed-51e52978481835413d15b589919aba80dd85f890.tar.gz/md5 @@ -0,0 +1 @@ +bf358a22ed4aa0d57b8672ef81fb2b44 diff --git a/deps/checksums/Distributed-51e52978481835413d15b589919aba80dd85f890.tar.gz/sha512 b/deps/checksums/Distributed-51e52978481835413d15b589919aba80dd85f890.tar.gz/sha512 new file mode 100644 index 0000000000000..171fd2fdbf512 --- /dev/null +++ b/deps/checksums/Distributed-51e52978481835413d15b589919aba80dd85f890.tar.gz/sha512 @@ -0,0 +1 @@ +399ab073d8c8cd1404e2e89ce2753486c782aa18646d56d8508c0a929c6a312e8bac2c791eddc01966f99cec1af34e56bbdccd8d75196b26f0b1bbb1fa8bd9ac diff --git a/deps/checksums/Distributed-8890288f01a3b4c2b64c87d98409bc9d865f506e.tar.gz/md5 b/deps/checksums/Distributed-8890288f01a3b4c2b64c87d98409bc9d865f506e.tar.gz/md5 deleted file mode 100644 index 3b640d12f3344..0000000000000 --- a/deps/checksums/Distributed-8890288f01a3b4c2b64c87d98409bc9d865f506e.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -7405afe10033da0431c8fd920a8cbbbf diff --git a/deps/checksums/Distributed-8890288f01a3b4c2b64c87d98409bc9d865f506e.tar.gz/sha512 b/deps/checksums/Distributed-8890288f01a3b4c2b64c87d98409bc9d865f506e.tar.gz/sha512 deleted file mode 100644 index e9003e31edcba..0000000000000 --- a/deps/checksums/Distributed-8890288f01a3b4c2b64c87d98409bc9d865f506e.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -ad3498cfee95bcd088e47c15eb2707f47ced9493881ec356cbeb22f66207406d23a3e3b27e70a00be7c2c755c6651f54f5378ef42bf4d1312c84d589010aab7b diff --git a/stdlib/Distributed.version b/stdlib/Distributed.version index 13d49b7a093de..be52c0d684b50 100644 --- a/stdlib/Distributed.version +++ b/stdlib/Distributed.version @@ -1,4 +1,4 @@ DISTRIBUTED_BRANCH = master -DISTRIBUTED_SHA1 = 8890288f01a3b4c2b64c87d98409bc9d865f506e +DISTRIBUTED_SHA1 = 51e52978481835413d15b589919aba80dd85f890 DISTRIBUTED_GIT_URL := https://github.com/JuliaLang/Distributed.jl DISTRIBUTED_TAR_URL = https://api.github.com/repos/JuliaLang/Distributed.jl/tarball/$1 From 856031880e7a223b6bf5c0875d843b88c0359495 Mon Sep 17 00:00:00 2001 From: Nick Robinson Date: Tue, 28 Jan 2025 14:30:58 +0000 Subject: [PATCH 36/56] Loosen type restriction on `Ryu.writeshortest` (#57172) `Ryu.writeshortest` is documented to have a method that "allows passing in a byte buffer", just like `Ryu.writefixed` and `Ryu.writeexp`, but unlike those functions `writeshortest` is type constrained to `::Vector{UInt8}`. This PR loosens that to `::AbstractVector{UInt8}`, to allow the "byte buffer" to e.g. be a `Memory` rather than a `Vector`. I've added tests and updated the docstrings for all three functions to ensure that they're not just restricted to `Vector{UInt8}`. This change was prompted by our private codebase hitting `MethodError: no method matching writeshortest(::Memory{UInt8}, ::Int64, ::Float64, ...)` when trying it out with Julia v1.12.0-DEV. (cc @quinnj -- i think you added this method originally, but i couldn't see any reason why e.g. Memory shouldn't be allowed now we have it) --- base/ryu/Ryu.jl | 6 +++--- base/ryu/shortest.jl | 2 +- test/ryu.jl | 51 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 55 insertions(+), 4 deletions(-) diff --git a/base/ryu/Ryu.jl b/base/ryu/Ryu.jl index e44e240baafda..86b9a64c002d5 100644 --- a/base/ryu/Ryu.jl +++ b/base/ryu/Ryu.jl @@ -19,7 +19,7 @@ neededdigits(::Type{Float16}) = 9 + 5 + 9 """ Ryu.writeshortest(x, plus=false, space=false, hash=true, precision=-1, expchar=UInt8('e'), padexp=false, decchar=UInt8('.'), typed=false, compact=false) - Ryu.writeshortest(buf::Vector{UInt8}, pos::Int, x, args...) + Ryu.writeshortest(buf::AbstractVector{UInt8}, pos::Int, x, args...) Convert a float value `x` into its "shortest" decimal string, which can be parsed back to the same value. This function allows achieving the `%g` printf format. @@ -53,7 +53,7 @@ end """ Ryu.writefixed(x, precision, plus=false, space=false, hash=false, decchar=UInt8('.'), trimtrailingzeros=false) - Ryu.writefixed(buf::Vector{UInt8}, pos::Int, x, args...) + Ryu.writefixed(buf::AbstractVector{UInt8}, pos::Int, x, args...) Convert a float value `x` into a "fixed" size decimal string of the provided precision. This function allows achieving the `%f` printf format. @@ -81,7 +81,7 @@ end """ Ryu.writeexp(x, precision, plus=false, space=false, hash=false, expchar=UInt8('e'), decchar=UInt8('.'), trimtrailingzeros=false) - Ryu.writeexp(buf::Vector{UInt8}, pos::Int, x, args...) + Ryu.writeexp(buf::AbstractVector{UInt8}, pos::Int, x, args...) Convert a float value `x` into a scientific notation decimal string. This function allows achieving the `%e` printf format. diff --git a/base/ryu/shortest.jl b/base/ryu/shortest.jl index 32aa993467e7a..c1ec648bfacdd 100644 --- a/base/ryu/shortest.jl +++ b/base/ryu/shortest.jl @@ -224,7 +224,7 @@ integer. If a `maxsignif` argument is provided, then `b < maxsignif`. return b, e10 end -function writeshortest(buf::Vector{UInt8}, pos, x::T, +function writeshortest(buf::AbstractVector{UInt8}, pos, x::T, plus=false, space=false, hash=true, precision=-1, expchar=UInt8('e'), padexp=false, decchar=UInt8('.'), typed=false, compact=false) where {T} diff --git a/test/ryu.jl b/test/ryu.jl index 4acd2fd08df50..05eedef9a0da2 100644 --- a/test/ryu.jl +++ b/test/ryu.jl @@ -370,6 +370,23 @@ end end # Float16 +@testset "writeshortest(::AbstractVector, pos, ...)" begin + @testset for Vec in (Vector{UInt8}, Memory{UInt8}) + buf = Vec(undef, 4) + @test Ryu.writeshortest(buf, 1, -0.0) == 5 + @test String(buf) == "-0.0" + + buf = Vec(undef, 100) + xx = 4.7223665f21 + expected = "4.7223665e21" + start_pos = 42 + nwritten = length(expected) + end_pos = start_pos + nwritten + @test Ryu.writeshortest(buf, start_pos, xx) == end_pos + @test String(buf[start_pos:end_pos-1]) == expected + end +end + @testset "Ryu.writefixed" begin @testset "Basic" begin @test Ryu.writefixed(todouble(false, 1234, 99999), 0) == @@ -563,6 +580,23 @@ end # Float16 @test Ryu.writefixed(-100.0+eps(-100.0), 0, false, false, true, UInt8('.'), false) == "-100." @test Ryu.writefixed(100.0-eps(100.0), 1, false, false, true, UInt8('.'), false) == "100.0" @test Ryu.writefixed(-100.0+eps(-100.0), 1, false, false, true, UInt8('.'), false) == "-100.0" + + @testset "writefixed(::AbstractVector, pos, ...)" begin + @testset for Vec in (Vector{UInt8}, Memory{UInt8}) + buf = Vec(undef, 6) + @test Ryu.writefixed(buf, 1, 0.0, 4) == 7 + @test String(buf) == "0.0000" + + buf = Vec(undef, 100) + xx = 1729.142857142857 + prec = 8 + start_pos = 42 + nwritten = 4 + 1 + prec + end_pos = start_pos + nwritten + @test Ryu.writefixed(buf, start_pos, xx, prec) == end_pos + @test String(buf[start_pos:end_pos-1]) == "1729.14285714" + end + end end # fixed @testset "Ryu.writeexp" begin @@ -761,6 +795,23 @@ end @test Ryu.writeexp(2.0, 1, false, false, false, UInt8('e'), UInt8('.'), true) == "2e+00" end +@testset "writeexp(::AbstractVector, pos, ...)" begin + @testset for Vec in (Vector{UInt8}, Memory{UInt8}) + buf = Vec(undef, 10) + @test Ryu.writeexp(buf, 1, 0.0, 4) == 11 + @test String(buf) == "0.0000e+00" + + buf = Vec(undef, 100) + xx = 1729.142857142857 + prec = 8 + start_pos = 42 + nwritten = 1 + 1 + prec + 4 + end_pos = start_pos + nwritten + @test Ryu.writeexp(buf, start_pos, xx, prec) == end_pos + @test String(buf[start_pos:end_pos-1]) == "1.72914286e+03" + end +end + end # exp @testset "compact" begin From 575d8e805d2b0b2cc2213b7745f3a90cef190b0c Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Tue, 28 Jan 2025 10:12:29 -0500 Subject: [PATCH 37/56] Add `Timer` args to struct and add show method (#57081) --- NEWS.md | 1 + base/asyncevent.jl | 44 ++++++++++++++++++++++++++++++++++++++++++-- test/channels.jl | 10 ++++++++++ 3 files changed, 53 insertions(+), 2 deletions(-) diff --git a/NEWS.md b/NEWS.md index 796fe9647fff0..1a3ba07021bad 100644 --- a/NEWS.md +++ b/NEWS.md @@ -128,6 +128,7 @@ New library features certain compiler plugin workflows ([#56660]). * `sort` now supports `NTuple`s ([#54494]) * `map!(f, A)` now stores the results in `A`, like `map!(f, A, A)`. or `A .= f.(A)` ([#40632]). +* `Timer` now has readable `timeout` and `interval` properties, and a more descriptive show method ([#57081]) Standard library changes ------------------------ diff --git a/base/asyncevent.jl b/base/asyncevent.jl index 8c708455976e2..8297626ee0f97 100644 --- a/base/asyncevent.jl +++ b/base/asyncevent.jl @@ -74,7 +74,24 @@ Create a timer that wakes up tasks waiting for it (by calling [`wait`](@ref) on Waiting tasks are woken after an initial delay of at least `delay` seconds, and then repeating after at least `interval` seconds again elapse. If `interval` is equal to `0`, the timer is only triggered once. When the timer is closed (by [`close`](@ref)) waiting tasks are woken with an error. Use -[`isopen`](@ref) to check whether a timer is still active. +[`isopen`](@ref) to check whether a timer is still active. Use `t.timeout` and `t.interval` to read +the setup conditions of a `Timer` `t`. + +```julia-repl +julia> t = Timer(1.0; interval=0.5) +Timer (open, timeout: 1.0 s, interval: 0.5 s) @0x000000010f4e6e90 + +julia> isopen(t) +true + +julia> t.timeout +1.0 + +julia> close(t) + +julia> isopen(t) +false +``` !!! note `interval` is subject to accumulating time skew. If you need precise events at a particular @@ -84,12 +101,17 @@ once. When the timer is closed (by [`close`](@ref)) waiting tasks are woken with A `Timer` requires yield points to update its state. For instance, `isopen(t::Timer)` cannot be used to timeout a non-yielding while loop. +!!! compat "Julia 1.12 + The `timeout` and `interval` readable properties were added in Julia 1.12. + """ mutable struct Timer @atomic handle::Ptr{Cvoid} cond::ThreadSynchronizer @atomic isopen::Bool @atomic set::Bool + timeout_ms::UInt64 + interval_ms::UInt64 function Timer(timeout::Real; interval::Real = 0.0) timeout ≥ 0 || throw(ArgumentError("timer cannot have negative timeout of $timeout seconds")) @@ -99,7 +121,7 @@ mutable struct Timer intervalms = ceil(UInt64, interval * 1000) loop = eventloop() - this = new(Libc.malloc(_sizeof_uv_timer), ThreadSynchronizer(), true, false) + this = new(Libc.malloc(_sizeof_uv_timer), ThreadSynchronizer(), true, false, timeoutms, intervalms) associate_julia_struct(this.handle, this) iolock_begin() err = ccall(:uv_timer_init, Cint, (Ptr{Cvoid}, Ptr{Cvoid}), loop, this) @@ -114,6 +136,24 @@ mutable struct Timer return this end end +function getproperty(t::Timer, f::Symbol) + if f == :timeout + t.timeout_ms == 0 && return 0.0 + return (t.timeout_ms - 1) / 1000 # remove the +1ms compensation from the constructor + elseif f == :interval + return t.interval_ms / 1000 + else + return getfield(t, f) + end +end +propertynames(::Timer) = (:handle, :cond, :isopen, :set, :timeout, :timeout_ms, :interval, :interval_ms) + +function show(io::IO, t::Timer) + state = isopen(t) ? "open" : "closed" + interval = t.interval + interval_str = interval > 0 ? ", interval: $(t.interval) s" : "" + print(io, "Timer ($state, timeout: $(t.timeout) s$interval_str) @0x$(string(convert(UInt, pointer_from_objref(t)), base = 16, pad = Sys.WORD_SIZE>>2))") +end unsafe_convert(::Type{Ptr{Cvoid}}, t::Timer) = t.handle unsafe_convert(::Type{Ptr{Cvoid}}, async::AsyncCondition) = async.handle diff --git a/test/channels.jl b/test/channels.jl index d654bc63be586..f646b41cfa1a0 100644 --- a/test/channels.jl +++ b/test/channels.jl @@ -621,6 +621,16 @@ let a = Ref(0) @test a[] == 1 end +@testset "Timer properties" begin + t = Timer(1.0, interval = 0.5) + @test t.timeout == 1.0 + @test t.interval == 0.5 + close(t) + @test !isopen(t) + @test t.timeout == 1.0 + @test t.interval == 0.5 +end + # trying to `schedule` a finished task let t = @async nothing wait(t) From 7a97bc91cf7d4223fd05940b322774476f5913a7 Mon Sep 17 00:00:00 2001 From: Cody Tapscott <84105208+topolarity@users.noreply.github.com> Date: Tue, 28 Jan 2025 15:09:53 -0500 Subject: [PATCH 38/56] interpreter: pop GC frames when leaving multiple `:enter` blocks (#57186) Resolves https://github.com/JuliaLang/julia/issues/56062 --- src/interpreter.c | 8 +++++++- test/scopedvalues.jl | 13 +++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/interpreter.c b/src/interpreter.c index 338853b56f692..35c70a9ead2f1 100644 --- a/src/interpreter.c +++ b/src/interpreter.c @@ -540,6 +540,9 @@ static jl_value_t *eval_body(jl_array_t *stmts, interpreter_state *s, size_t ip, s->locals[jl_source_nslots(s->src) + ip] = jl_box_ulong(jl_excstack_state(ct)); if (jl_enternode_scope(stmt)) { jl_value_t *scope = eval_value(jl_enternode_scope(stmt), s); + // GC preserve the scope, since it is not rooted in the `jl_handler_t *` + // and may be removed from jl_current_task by any nested block and then + // replaced later JL_GC_PUSH1(&scope); ct->scope = scope; if (!jl_setjmp(__eh.eh_ctx, 1)) { @@ -606,8 +609,11 @@ static jl_value_t *eval_body(jl_array_t *stmts, interpreter_state *s, size_t ip, // equivalent to jl_pop_handler(hand_n_leave), longjmping // to the :enter code above instead, which handles cleanup jl_handler_t *eh = ct->eh; - while (--hand_n_leave > 0) + while (--hand_n_leave > 0) { + // pop GC frames for any skipped handlers + ct->gcstack = eh->gcstack; eh = eh->prev; + } // leave happens during normal control flow, but we must // longjmp to pop the eval_body call for each enter. s->continue_at = next_ip; diff --git a/test/scopedvalues.jl b/test/scopedvalues.jl index 174bc690ac0a2..e9b36d80fc2c4 100644 --- a/test/scopedvalues.jl +++ b/test/scopedvalues.jl @@ -182,3 +182,16 @@ end @test Core.current_scope() === nothing end nothrow_scope() + +# https://github.com/JuliaLang/julia/issues/56062 +@testset "issue #56062" begin + ts = Int[] + try + @with begin + return + end + catch err + finally + push!(ts, 2) + end +end From 989b32b6e2483a16bf2abe291defde19a2cb94d7 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Tue, 28 Jan 2025 19:50:02 -0500 Subject: [PATCH 39/56] CI: Be noisy about cache being bad. Check hash of downloaded busybox executable (#57174) --- test/spawn.jl | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/test/spawn.jl b/test/spawn.jl index 0356cf9871424..099f0670ce5f7 100644 --- a/test/spawn.jl +++ b/test/spawn.jl @@ -4,7 +4,7 @@ # Cross Platform tests for spawn. # ################################### -using Random, Sockets +using Random, Sockets, SHA using Downloads: Downloads, download valgrind_off = ccall(:jl_running_on_valgrind, Cint, ()) == 0 @@ -21,6 +21,8 @@ sleepcmd = `sleep` lscmd = `ls` havebb = false +busybox_hash_correct(file) = bytes2hex(open(SHA.sha256, file)) == "ed2f95da9555268e93c7af52feb48e148534ee518b9128f65dda9a2767b61b9e" + function _tryonce_download_from_cache(desired_url::AbstractString) cache_url = "https://cache.julialang.org/$(desired_url)" cache_output_filename = joinpath(mktempdir(), "busybox") @@ -32,9 +34,14 @@ function _tryonce_download_from_cache(desired_url::AbstractString) ) if cache_response isa Downloads.Response if Downloads.status_ok(cache_response.proto, cache_response.status) - return cache_output_filename + if busybox_hash_correct(cache_output_filename) + return cache_output_filename + else + @warn "The busybox executable downloaded from the cache has an incorrect hash" cache_output_filename bytes2hex(open(SHA.sha256, cache_output_filename)) + end end end + @warn "Could not download from cache at $cache_url, falling back to primary source at $desired_url" return Downloads.download(desired_url; timeout = 60) end @@ -46,7 +53,11 @@ function download_from_cache(desired_url::AbstractString) end if Sys.iswindows() - busybox = download_from_cache("https://frippery.org/files/busybox/busybox.exe") + # See https://frippery.org/files/busybox/ + # latest as of 2024-09-20 18:08 + busybox = download_from_cache("https://frippery.org/files/busybox/busybox-w32-FRP-5467-g9376eebd8.exe") + busybox_hash_correct(busybox) || error("The busybox executable downloaded has an incorrect hash") + havebb = try # use busybox-w32 on windows, if available success(`$busybox`) true From f209eba244d55afbf7aeff298434deba4fcbe30a Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Wed, 29 Jan 2025 03:08:35 +0100 Subject: [PATCH 40/56] bpart: Start enforcing min_world for global variable definitions (#57150) This is the analog of #57102 for global variables. Unlike for consants, there is no automatic global backdate mechanism. The reasoning for this is that global variables can be declared at any time, unlike constants which can only be decalared once their value is available. As a result code patterns using `Core.eval` to declare globals are rarer and likely incorrect. --- Compiler/test/abioverride.jl | 2 +- base/docs/Docs.jl | 17 ++++-- base/exports.jl | 1 + base/loading.jl | 2 +- src/builtins.c | 10 ++-- src/codegen.cpp | 103 ++++----------------------------- src/jl_exported_funcs.inc | 1 + src/julia-syntax.scm | 11 +++- src/julia.h | 3 +- src/module.c | 44 +++++++------- src/toplevel.c | 107 ++++++++++++++++++++--------------- stdlib/REPL/src/REPL.jl | 28 ++++++--- stdlib/REPL/test/repl.jl | 14 ++--- sysimage.mk | 1 + test/core.jl | 1 + test/precompile.jl | 2 +- test/worlds.jl | 29 ++++++---- 17 files changed, 175 insertions(+), 201 deletions(-) diff --git a/Compiler/test/abioverride.jl b/Compiler/test/abioverride.jl index 49907ea8e4c63..feb992b27ee43 100644 --- a/Compiler/test/abioverride.jl +++ b/Compiler/test/abioverride.jl @@ -46,7 +46,7 @@ let world = Base.tls_world_age() global new_ci = Core.CodeInstance(Core.ABIOverride(Tuple{typeof(myplus), Int}, mi), #=owner=#SecondArgConstOverride(1), new_source.rettype, Any#=new_source.exctype is missing=#, #=inferred_const=#nothing, #=code=#nothing, #=const_flags=#Int32(0), - new_source.min_world, new_source.max_world, #=new_source.ipo_purity_bits is missing=#UInt32(0), + new_source.min_world, typemax(UInt), #=new_source.ipo_purity_bits is missing=#UInt32(0), #=analysis_results=#nothing, new_source.debuginfo, new_source.edges) # Poke the CI into the global cache diff --git a/base/docs/Docs.jl b/base/docs/Docs.jl index 61c0cf71e70c2..061a94bffd9cf 100644 --- a/base/docs/Docs.jl +++ b/base/docs/Docs.jl @@ -75,18 +75,23 @@ const META = gensym(:meta) const METAType = IdDict{Any,Any} function meta(m::Module; autoinit::Bool=true) - if !isdefined(m, META) || getfield(m, META) === nothing - autoinit ? initmeta(m) : return nothing + if !isdefinedglobal(m, META) + return autoinit ? invokelatest(initmeta, m) : nothing end - return getfield(m, META)::METAType + # TODO: This `invokelatest` is not technically required, but because + # of the automatic constant backdating is currently required to avoid + # a warning. + return invokelatest(getglobal, m, META)::METAType end function initmeta(m::Module) - if !isdefined(m, META) || getfield(m, META) === nothing - Core.eval(m, :($META = $(METAType()))) + if !isdefinedglobal(m, META) + val = METAType() + Core.eval(m, :(const $META = $val)) push!(modules, m) + return val end - nothing + return getglobal(m, META) end function signature!(tv::Vector{Any}, expr::Expr) diff --git a/base/exports.jl b/base/exports.jl index 56cd58ce269e7..d81067478dd55 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -818,6 +818,7 @@ export @invoke, invokelatest, @invokelatest, + @world, # loading source files __precompile__, diff --git a/base/loading.jl b/base/loading.jl index 240406292246b..57d69f49483c9 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -1389,7 +1389,7 @@ function register_restored_modules(sv::SimpleVector, pkg::PkgId, path::String) restored = sv[1]::Vector{Any} for M in restored M = M::Module - if isdefined(M, Base.Docs.META) && getfield(M, Base.Docs.META) !== nothing + if isdefinedglobal(M, Base.Docs.META) push!(Base.Docs.modules, M) end if is_root_module(M) diff --git a/src/builtins.c b/src/builtins.c index 90d8a0d453e20..f67ef65d35356 100644 --- a/src/builtins.c +++ b/src/builtins.c @@ -1395,7 +1395,7 @@ JL_CALLABLE(jl_f_setglobal) jl_atomic_error("setglobal!: module binding cannot be written non-atomically"); else if (order >= jl_memory_order_seq_cst) jl_fence(); - jl_binding_t *b = jl_get_binding_wr(mod, var, 0); + jl_binding_t *b = jl_get_binding_wr(mod, var); jl_checked_assignment(b, mod, var, args[2]); // release store if (order >= jl_memory_order_seq_cst) jl_fence(); @@ -1430,7 +1430,7 @@ JL_CALLABLE(jl_f_swapglobal) if (order == jl_memory_order_notatomic) jl_atomic_error("swapglobal!: module binding cannot be written non-atomically"); // is seq_cst already, no fence needed - jl_binding_t *b = jl_get_binding_wr(mod, var, 0); + jl_binding_t *b = jl_get_binding_wr(mod, var); return jl_checked_swap(b, mod, var, args[2]); } @@ -1448,7 +1448,7 @@ JL_CALLABLE(jl_f_modifyglobal) JL_TYPECHK(modifyglobal!, symbol, (jl_value_t*)var); if (order == jl_memory_order_notatomic) jl_atomic_error("modifyglobal!: module binding cannot be written non-atomically"); - jl_binding_t *b = jl_get_binding_wr(mod, var, 0); + jl_binding_t *b = jl_get_binding_wr(mod, var); // is seq_cst already, no fence needed return jl_checked_modify(b, mod, var, args[2], args[3]); } @@ -1477,7 +1477,7 @@ JL_CALLABLE(jl_f_replaceglobal) jl_atomic_error("replaceglobal!: module binding cannot be written non-atomically"); if (failure_order == jl_memory_order_notatomic) jl_atomic_error("replaceglobal!: module binding cannot be accessed non-atomically"); - jl_binding_t *b = jl_get_binding_wr(mod, var, 0); + jl_binding_t *b = jl_get_binding_wr(mod, var); // is seq_cst already, no fence needed return jl_checked_replace(b, mod, var, args[2], args[3]); } @@ -1506,7 +1506,7 @@ JL_CALLABLE(jl_f_setglobalonce) jl_atomic_error("setglobalonce!: module binding cannot be written non-atomically"); if (failure_order == jl_memory_order_notatomic) jl_atomic_error("setglobalonce!: module binding cannot be accessed non-atomically"); - jl_binding_t *b = jl_get_binding_wr(mod, var, 0); + jl_binding_t *b = jl_get_binding_wr(mod, var); // is seq_cst already, no fence needed jl_value_t *old = jl_checked_assignonce(b, mod, var, args[2]); return old == NULL ? jl_true : jl_false; diff --git a/src/codegen.cpp b/src/codegen.cpp index 19ee1e9161152..e9e4275672c7e 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -930,12 +930,12 @@ static const auto jlgetbindingorerror_func = new JuliaFunction<>{ }, nullptr, }; -static const auto jlgetbindingwrorerror_func = new JuliaFunction<>{ - XSTR(jl_get_binding_wr), +static const auto jlcheckbpwritable_func = new JuliaFunction<>{ + XSTR(jl_check_binding_currently_writable), [](LLVMContext &C) { auto T_pjlvalue = JuliaType::get_pjlvalue_ty(C); - return FunctionType::get(T_pjlvalue, - {T_pjlvalue, T_pjlvalue, getInt32Ty(C)}, false); + return FunctionType::get(getVoidTy(C), + {T_pjlvalue, T_pjlvalue, T_pjlvalue}, false); }, nullptr, }; @@ -2098,8 +2098,6 @@ static Type *julia_type_to_llvm(jl_codectx_t &ctx, jl_value_t *jt, bool *isboxed static jl_returninfo_t get_specsig_function(jl_codectx_t &ctx, Module *M, Value *fval, StringRef name, jl_value_t *sig, jl_value_t *jlrettype, bool is_opaque_closure, bool gcstack_arg, ArrayRef ArgNames=None, unsigned nreq=0); static jl_cgval_t emit_expr(jl_codectx_t &ctx, jl_value_t *expr, ssize_t ssaval = -1); -static Value *global_binding_pointer(jl_codectx_t &ctx, jl_module_t *m, jl_sym_t *s, - jl_binding_t **pbnd, bool assign, bool alloc); static jl_cgval_t emit_checked_var(jl_codectx_t &ctx, Value *bp, jl_sym_t *name, jl_value_t *scope, bool isvol, MDNode *tbaa); static jl_cgval_t emit_sparam(jl_codectx_t &ctx, size_t i); static Value *emit_condition(jl_codectx_t &ctx, const jl_cgval_t &condV, const Twine &msg); @@ -3498,19 +3496,17 @@ static jl_cgval_t emit_globalop(jl_codectx_t &ctx, jl_module_t *mod, jl_sym_t *s bool issetglobal, bool isreplaceglobal, bool isswapglobal, bool ismodifyglobal, bool issetglobalonce, const jl_cgval_t *modifyop, bool alloc) { - jl_binding_t *bnd = NULL; - Value *bp = global_binding_pointer(ctx, mod, sym, &bnd, true, alloc); + jl_binding_t *bnd = jl_get_module_binding(mod, sym, 1); jl_binding_partition_t *bpart = jl_get_binding_partition_all(bnd, ctx.min_world, ctx.max_world); - if (bp == NULL) - return jl_cgval_t(); + Value *bp = julia_binding_gv(ctx, bnd); if (bpart) { jl_ptr_kind_union_t pku = jl_atomic_load_relaxed(&bpart->restriction); - if (!jl_bkind_is_some_constant(decode_restriction_kind(pku))) { + if (decode_restriction_kind(pku) == BINDING_KIND_GLOBAL) { jl_value_t *ty = decode_restriction_value(pku); if (ty != nullptr) { const std::string fname = issetglobal ? "setglobal!" : isreplaceglobal ? "replaceglobal!" : isswapglobal ? "swapglobal!" : ismodifyglobal ? "modifyglobal!" : "setglobalonce!"; if (!ismodifyglobal) { - // TODO: use typeassert in jl_check_binding_wr too + // TODO: use typeassert in jl_check_binding_assign_value too emit_typecheck(ctx, rval, ty, "typeassert"); rval = update_julia_type(ctx, rval, ty); if (rval.typ == jl_bottom_type) @@ -3545,6 +3541,8 @@ static jl_cgval_t emit_globalop(jl_codectx_t &ctx, jl_module_t *mod, jl_sym_t *s } Value *m = literal_pointer_val(ctx, (jl_value_t*)mod); Value *s = literal_pointer_val(ctx, (jl_value_t*)sym); + ctx.builder.CreateCall(prepare_call(jlcheckbpwritable_func), + { bp, m, s }); if (issetglobal) { ctx.builder.CreateCall(prepare_call(jlcheckassign_func), { bp, m, s, mark_callee_rooted(ctx, boxed(ctx, rval)) }); @@ -5991,85 +5989,6 @@ static void emit_hasnofield_error_ifnot(jl_codectx_t &ctx, Value *ok, jl_datatyp ctx.builder.SetInsertPoint(ifok); } -// returns a jl_ppvalue_t location for the global variable m.s -// if the reference currently bound or assign == true, -// pbnd will also be assigned with the binding address -static Value *global_binding_pointer(jl_codectx_t &ctx, jl_module_t *m, jl_sym_t *s, - jl_binding_t **pbnd, bool assign, bool alloc) -{ - jl_binding_t *b = jl_get_module_binding(m, s, 1); - jl_binding_partition_t *bpart = jl_get_binding_partition_all(b, ctx.min_world, ctx.max_world); - jl_ptr_kind_union_t pku = jl_atomic_load_relaxed(&bpart->restriction); - if (assign) { - if (jl_bkind_is_some_guard(decode_restriction_kind(pku))) - // not yet declared - b = NULL; - } - else { - if (jl_bkind_is_some_guard(decode_restriction_kind(pku))) { - // try to look this up now - b = jl_get_binding(m, s); - bpart = jl_get_binding_partition_all(b, ctx.min_world, ctx.max_world); - } - pku = jl_walk_binding_inplace_all(&b, &bpart, ctx.min_world, ctx.max_world); - } - if (!b || !bpart) { - // var not found. switch to delayed lookup. - Constant *initnul = Constant::getNullValue(ctx.types().T_pjlvalue); - GlobalVariable *bindinggv = new GlobalVariable(*ctx.f->getParent(), ctx.types().T_pjlvalue, - false, GlobalVariable::PrivateLinkage, initnul, "jl_binding_ptr"); // LLVM has bugs with nameless globals - LoadInst *cachedval = ctx.builder.CreateAlignedLoad(ctx.types().T_pjlvalue, bindinggv, Align(sizeof(void*))); - setName(ctx.emission_context, cachedval, jl_symbol_name(m->name) + StringRef(".") + jl_symbol_name(s) + ".cached"); - cachedval->setOrdering(AtomicOrdering::Unordered); - BasicBlock *have_val = BasicBlock::Create(ctx.builder.getContext(), "found"); - BasicBlock *not_found = BasicBlock::Create(ctx.builder.getContext(), "notfound"); - BasicBlock *currentbb = ctx.builder.GetInsertBlock(); - auto iscached = ctx.builder.CreateICmpNE(cachedval, initnul); - setName(ctx.emission_context, iscached, "iscached"); - ctx.builder.CreateCondBr(iscached, have_val, not_found); - not_found->insertInto(ctx.f); - ctx.builder.SetInsertPoint(not_found); - Value *bval = nullptr; - if (assign) { - bval = ctx.builder.CreateCall(prepare_call(jlgetbindingwrorerror_func), - { literal_pointer_val(ctx, (jl_value_t*)m), - literal_pointer_val(ctx, (jl_value_t*)s), - ConstantInt::get(getInt32Ty(ctx.builder.getContext()), alloc)}); - } else { - bval = ctx.builder.CreateCall(prepare_call(jlgetbindingorerror_func), - { literal_pointer_val(ctx, (jl_value_t*)m), - literal_pointer_val(ctx, (jl_value_t*)s)}); - } - setName(ctx.emission_context, bval, jl_symbol_name(m->name) + StringRef(".") + jl_symbol_name(s) + ".found"); - ctx.builder.CreateAlignedStore(bval, bindinggv, Align(sizeof(void*)))->setOrdering(AtomicOrdering::Release); - ctx.builder.CreateBr(have_val); - have_val->insertInto(ctx.f); - ctx.builder.SetInsertPoint(have_val); - PHINode *p = ctx.builder.CreatePHI(ctx.types().T_pjlvalue, 2); - p->addIncoming(cachedval, currentbb); - p->addIncoming(bval, not_found); - setName(ctx.emission_context, p, jl_symbol_name(m->name) + StringRef(".") + jl_symbol_name(s)); - return p; - } - if (assign) { - if (decode_restriction_kind(pku) != BINDING_KIND_GLOBAL && !jl_bkind_is_some_guard(decode_restriction_kind(pku))) { - // this will fail at runtime, so defer to the runtime to create the error - ctx.builder.CreateCall(prepare_call(jlgetbindingwrorerror_func), - { literal_pointer_val(ctx, (jl_value_t*)m), - literal_pointer_val(ctx, (jl_value_t*)s), - ConstantInt::get(getInt32Ty(ctx.builder.getContext()), alloc) }); - CreateTrap(ctx.builder); - return NULL; - } - } - else { - if (b->deprecated) - cg_bdw(ctx, s, b); - } - *pbnd = b; - return julia_binding_gv(ctx, b); -} - static jl_cgval_t emit_checked_var(jl_codectx_t &ctx, Value *bp, jl_sym_t *name, jl_value_t *scope, bool isvol, MDNode *tbaa) { LoadInst *v = ctx.builder.CreateAlignedLoad(ctx.types().T_prjlvalue, bp, Align(sizeof(void*))); @@ -10184,7 +10103,7 @@ static void init_jit_functions(void) add_named_global(jltypeerror_func, &jl_type_error); add_named_global(jlcheckassign_func, &jl_checked_assignment); add_named_global(jlgetbindingorerror_func, &jl_get_binding_or_error); - add_named_global(jlgetbindingwrorerror_func, &jl_get_binding_wr); + add_named_global(jlcheckbpwritable_func, &jl_check_binding_currently_writable); add_named_global(jlboundp_func, &jl_boundp); for (auto it : builtin_func_map()) add_named_global(it.second, it.first); diff --git a/src/jl_exported_funcs.inc b/src/jl_exported_funcs.inc index c1b29a091511b..9e221420aa9f4 100644 --- a/src/jl_exported_funcs.inc +++ b/src/jl_exported_funcs.inc @@ -196,6 +196,7 @@ XX(jl_get_binding_for_method_def) \ XX(jl_get_binding_or_error) \ XX(jl_get_binding_wr) \ + XX(jl_check_binding_currently_writable) \ XX(jl_get_cpu_name) \ XX(jl_get_cpu_features) \ XX(jl_cpu_has_fma) \ diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index 57f67755df692..97d76e7762a9e 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -4626,6 +4626,8 @@ f(x) = yt(x) (if (globalref? lhs) (begin (emit `(global ,lhs)) + (if (null? (cadr lam)) + (emit `(latestworld))) (emit `(call (top setglobal!) ,(cadr lhs) (inert ,(caddr lhs)) ,rhs))) (emit `(= ,lhs ,rhs)))) (define (emit-assignment lhs rhs) @@ -4944,13 +4946,16 @@ f(x) = yt(x) #f)) ((global) ; keep global declarations as statements (if value (error "misplaced \"global\" declaration")) - (emit e)) + (emit e) + (if (null? (cadr lam)) + (emit `(latestworld)))) ((globaldecl) (if value (error "misplaced \"global\" declaration")) - (if (atom? (caddr e)) (emit e) + (if (atom? (caddr e)) (begin (emit e) (emit `(latestworld))) (let ((rr (make-ssavalue))) (emit `(= ,rr ,(caddr e))) - (emit `(globaldecl ,(cadr e) ,rr))))) + (emit `(globaldecl ,(cadr e) ,rr)) + (emit `(latestworld))))) ((local-def) #f) ((local) #f) ((moved-local) diff --git a/src/julia.h b/src/julia.h index f36133088119f..a5f56ad580335 100644 --- a/src/julia.h +++ b/src/julia.h @@ -2001,7 +2001,8 @@ JL_DLLEXPORT jl_binding_t *jl_get_binding_or_error(jl_module_t *m, jl_sym_t *var JL_DLLEXPORT jl_value_t *jl_module_globalref(jl_module_t *m, jl_sym_t *var); JL_DLLEXPORT jl_value_t *jl_get_binding_type(jl_module_t *m, jl_sym_t *var); // get binding for assignment -JL_DLLEXPORT jl_binding_t *jl_get_binding_wr(jl_module_t *m JL_PROPAGATES_ROOT, jl_sym_t *var, int alloc); +JL_DLLEXPORT void jl_check_binding_currently_writable(jl_binding_t *b, jl_module_t *m, jl_sym_t *s); +JL_DLLEXPORT jl_binding_t *jl_get_binding_wr(jl_module_t *m JL_PROPAGATES_ROOT, jl_sym_t *var); JL_DLLEXPORT jl_binding_t *jl_get_binding_for_method_def(jl_module_t *m JL_PROPAGATES_ROOT, jl_sym_t *var); JL_DLLEXPORT int jl_boundp(jl_module_t *m, jl_sym_t *var, int allow_import); JL_DLLEXPORT int jl_defines_or_exports_p(jl_module_t *m, jl_sym_t *var); diff --git a/src/module.c b/src/module.c index f4c56e19efa61..2630db2dbfd94 100644 --- a/src/module.c +++ b/src/module.c @@ -279,38 +279,42 @@ extern void check_safe_newbinding(jl_module_t *m, jl_sym_t *var) static jl_module_t *jl_binding_dbgmodule(jl_binding_t *b, jl_module_t *m, jl_sym_t *var) JL_GLOBALLY_ROOTED; -// get binding for assignment -JL_DLLEXPORT jl_binding_t *jl_get_binding_wr(jl_module_t *m JL_PROPAGATES_ROOT, jl_sym_t *var, int alloc) +// Checks that the binding in general is currently writable, but does not perform any checks on the +// value to be written into the binding. +JL_DLLEXPORT void jl_check_binding_currently_writable(jl_binding_t *b, jl_module_t *m, jl_sym_t *s) { - jl_binding_t *b = jl_get_module_binding(m, var, 1); jl_binding_partition_t *bpart = jl_get_binding_partition(b, jl_current_task->world_age); jl_ptr_kind_union_t pku = jl_atomic_load_relaxed(&bpart->restriction); retry: if (decode_restriction_kind(pku) != BINDING_KIND_GLOBAL && !jl_bkind_is_some_constant(decode_restriction_kind(pku))) { if (jl_bkind_is_some_guard(decode_restriction_kind(pku))) { if (decode_restriction_kind(pku) != BINDING_KIND_DECLARED) { - check_safe_newbinding(m, var); - if (!alloc) - jl_errorf("Global %s.%s does not exist and cannot be assigned.\n" - "Note: Julia 1.9 and 1.10 inadvertently omitted this error check (#56933).\n" - "Hint: Declare it using `global %s` inside `%s` before attempting assignment.", - jl_symbol_name(m->name), jl_symbol_name(var), - jl_symbol_name(var), jl_symbol_name(m->name)); + jl_errorf("Global %s.%s does not exist and cannot be assigned.\n" + "Note: Julia 1.9 and 1.10 inadvertently omitted this error check (#56933).\n" + "Hint: Declare it using `global %s` inside `%s` before attempting assignment.", + jl_symbol_name(m->name), jl_symbol_name(s), + jl_symbol_name(s), jl_symbol_name(m->name)); } jl_ptr_kind_union_t new_pku = encode_restriction((jl_value_t*)jl_any_type, BINDING_KIND_GLOBAL); if (!jl_atomic_cmpswap(&bpart->restriction, &pku, new_pku)) goto retry; jl_gc_wb_knownold(bpart, jl_any_type); } else { - jl_module_t *from = jl_binding_dbgmodule(b, m, var); + jl_module_t *from = jl_binding_dbgmodule(b, m, s); if (from == m) jl_errorf("cannot assign a value to imported variable %s.%s", - jl_symbol_name(from->name), jl_symbol_name(var)); + jl_symbol_name(from->name), jl_symbol_name(s)); else jl_errorf("cannot assign a value to imported variable %s.%s from module %s", - jl_symbol_name(from->name), jl_symbol_name(var), jl_symbol_name(m->name)); + jl_symbol_name(from->name), jl_symbol_name(s), jl_symbol_name(m->name)); } } +} + +JL_DLLEXPORT jl_binding_t *jl_get_binding_wr(jl_module_t *m JL_PROPAGATES_ROOT, jl_sym_t *var) +{ + jl_binding_t *b = jl_get_module_binding(m, var, 1); + jl_check_binding_currently_writable(b, m, var); return b; } @@ -1066,7 +1070,7 @@ JL_DLLEXPORT jl_value_t *jl_get_global(jl_module_t *m, jl_sym_t *var) JL_DLLEXPORT void jl_set_global(jl_module_t *m JL_ROOTING_ARGUMENT, jl_sym_t *var, jl_value_t *val JL_ROOTED_ARGUMENT) { - jl_binding_t *bp = jl_get_binding_wr(m, var, 0); + jl_binding_t *bp = jl_get_binding_wr(m, var); jl_checked_assignment(bp, m, var, val); } @@ -1186,7 +1190,9 @@ void jl_binding_deprecation_warning(jl_module_t *m, jl_sym_t *s, jl_binding_t *b } } -jl_value_t *jl_check_binding_wr(jl_binding_t *b JL_PROPAGATES_ROOT, jl_module_t *mod, jl_sym_t *var, jl_value_t *rhs JL_MAYBE_UNROOTED, int reassign) +// For a generally writable binding (checked using jl_check_binding_currently_writable in this world age), check whether +// we can actually write the value `rhs` to it. +jl_value_t *jl_check_binding_assign_value(jl_binding_t *b JL_PROPAGATES_ROOT, jl_module_t *mod, jl_sym_t *var, jl_value_t *rhs JL_MAYBE_UNROOTED) { JL_GC_PUSH1(&rhs); // callee-rooted jl_binding_partition_t *bpart = jl_get_binding_partition(b, jl_current_task->world_age); @@ -1219,7 +1225,7 @@ jl_value_t *jl_check_binding_wr(jl_binding_t *b JL_PROPAGATES_ROOT, jl_module_t JL_DLLEXPORT void jl_checked_assignment(jl_binding_t *b, jl_module_t *mod, jl_sym_t *var, jl_value_t *rhs) { - if (jl_check_binding_wr(b, mod, var, rhs, 1) != NULL) { + if (jl_check_binding_assign_value(b, mod, var, rhs) != NULL) { jl_atomic_store_release(&b->value, rhs); jl_gc_wb(b, rhs); } @@ -1227,7 +1233,7 @@ JL_DLLEXPORT void jl_checked_assignment(jl_binding_t *b, jl_module_t *mod, jl_sy JL_DLLEXPORT jl_value_t *jl_checked_swap(jl_binding_t *b, jl_module_t *mod, jl_sym_t *var, jl_value_t *rhs) { - jl_check_binding_wr(b, mod, var, rhs, 0); + jl_check_binding_assign_value(b, mod, var, rhs); jl_value_t *old = jl_atomic_exchange(&b->value, rhs); jl_gc_wb(b, rhs); if (__unlikely(old == NULL)) @@ -1237,7 +1243,7 @@ JL_DLLEXPORT jl_value_t *jl_checked_swap(jl_binding_t *b, jl_module_t *mod, jl_s JL_DLLEXPORT jl_value_t *jl_checked_replace(jl_binding_t *b, jl_module_t *mod, jl_sym_t *var, jl_value_t *expected, jl_value_t *rhs) { - jl_value_t *ty = jl_check_binding_wr(b, mod, var, rhs, 0); + jl_value_t *ty = jl_check_binding_assign_value(b, mod, var, rhs); return replace_value(ty, &b->value, (jl_value_t*)b, expected, rhs, 1, mod, var); } @@ -1256,7 +1262,7 @@ JL_DLLEXPORT jl_value_t *jl_checked_modify(jl_binding_t *b, jl_module_t *mod, jl JL_DLLEXPORT jl_value_t *jl_checked_assignonce(jl_binding_t *b, jl_module_t *mod, jl_sym_t *var, jl_value_t *rhs ) { - jl_check_binding_wr(b, mod, var, rhs, 0); + jl_check_binding_assign_value(b, mod, var, rhs); jl_value_t *old = NULL; if (jl_atomic_cmpswap(&b->value, &old, rhs)) jl_gc_wb(b, rhs); diff --git a/src/toplevel.c b/src/toplevel.c index 30e78bf813fc8..35ad001ac4b4b 100644 --- a/src/toplevel.c +++ b/src/toplevel.c @@ -302,38 +302,6 @@ static jl_value_t *jl_eval_dot_expr(jl_module_t *m, jl_value_t *x, jl_value_t *f return args[0]; } -void jl_binding_set_type(jl_binding_t *b, jl_module_t *mod, jl_sym_t *sym, jl_value_t *ty) -{ - jl_binding_partition_t *bpart = jl_get_binding_partition(b, jl_current_task->world_age); - jl_ptr_kind_union_t pku = jl_atomic_load_relaxed(&bpart->restriction); - jl_ptr_kind_union_t new_pku = encode_restriction(ty, BINDING_KIND_GLOBAL); - while (1) { - if (decode_restriction_kind(pku) != BINDING_KIND_GLOBAL) { - if (jl_bkind_is_some_guard(decode_restriction_kind(pku))) { - if (jl_atomic_cmpswap(&bpart->restriction, &pku, new_pku)) - break; - continue; - } else { - jl_errorf("cannot set type for imported global %s.%s.", - jl_symbol_name(mod->name), jl_symbol_name(sym)); - } - } - if (jl_bkind_is_some_constant(decode_restriction_kind(pku))) { - jl_errorf("cannot set type for imported constant %s.%s.", - jl_symbol_name(mod->name), jl_symbol_name(sym)); - } - jl_value_t *old_ty = decode_restriction_value(pku); - JL_GC_PROMISE_ROOTED(old_ty); - if (!jl_types_equal(ty, old_ty)) { - jl_errorf("cannot set type for global %s.%s. It already has a value or is already set to a different type.", - jl_symbol_name(mod->name), jl_symbol_name(sym)); - } - if (jl_atomic_cmpswap(&bpart->restriction, &pku, new_pku)) - break; - } - jl_gc_wb(bpart, ty); -} - extern void check_safe_newbinding(jl_module_t *m, jl_sym_t *var); void jl_declare_global(jl_module_t *m, jl_value_t *arg, jl_value_t *set_type) { // create uninitialized mutable binding for "global x" decl sometimes or probably @@ -349,17 +317,49 @@ void jl_declare_global(jl_module_t *m, jl_value_t *arg, jl_value_t *set_type) { gm = m; gs = (jl_sym_t*)arg; } + JL_LOCK(&world_counter_lock); + size_t new_world = jl_atomic_load_relaxed(&jl_world_counter) + 1; jl_binding_t *b = jl_get_module_binding(gm, gs, 1); - jl_binding_partition_t *bpart = jl_get_binding_partition(b, jl_current_task->world_age); - jl_ptr_kind_union_t pku = jl_atomic_load_relaxed(&bpart->restriction); - while (decode_restriction_kind(pku) == BINDING_KIND_GUARD || decode_restriction_kind(pku) == BINDING_KIND_FAILED) { - check_safe_newbinding(gm, gs); - if (jl_atomic_cmpswap(&bpart->restriction, &pku, encode_restriction(NULL, BINDING_KIND_DECLARED))) - break; - } - if (set_type) { - jl_binding_set_type(b, gm, gs, set_type); + jl_binding_partition_t *bpart = NULL; + jl_ptr_kind_union_t new_pku = encode_restriction(set_type, set_type == NULL ? BINDING_KIND_DECLARED : BINDING_KIND_GLOBAL); + while (1) { + bpart = jl_get_binding_partition(b, new_world); + jl_ptr_kind_union_t pku = jl_atomic_load_relaxed(&bpart->restriction); + if (decode_restriction_kind(pku) != BINDING_KIND_GLOBAL) { + if (jl_bkind_is_some_guard(decode_restriction_kind(pku))) { + if (decode_restriction_kind(pku) == BINDING_KIND_DECLARED && !set_type) + goto done; + check_safe_newbinding(gm, gs); + if (jl_atomic_cmpswap(&bpart->restriction, &pku, new_pku)) { + break; + } + continue; + } else if (set_type) { + if (jl_bkind_is_some_constant(decode_restriction_kind(pku))) { + jl_errorf("cannot set type for imported constant %s.%s.", + jl_symbol_name(gm->name), jl_symbol_name(gs)); + } else { + jl_errorf("cannot set type for imported global %s.%s.", + jl_symbol_name(gm->name), jl_symbol_name(gs)); + } + } + } + if (!set_type) + goto done; + jl_value_t *old_ty = decode_restriction_value(pku); + JL_GC_PROMISE_ROOTED(old_ty); + if (!jl_types_equal(set_type, old_ty)) { + jl_errorf("cannot set type for global %s.%s. It already has a value or is already set to a different type.", + jl_symbol_name(gm->name), jl_symbol_name(gs)); + } + goto done; } + if (set_type) + jl_gc_wb(bpart, set_type); + bpart->min_world = new_world; + jl_atomic_store_release(&jl_world_counter, new_world); +done: + JL_UNLOCK(&world_counter_lock); } void jl_eval_global_expr(jl_module_t *m, jl_expr_t *ex, int set_type) @@ -751,7 +751,8 @@ JL_DLLEXPORT jl_binding_partition_t *jl_declare_constant_val3( jl_ptr_kind_union_t pku = jl_atomic_load_relaxed(&bpart->restriction); int did_warn = 0; while (1) { - if (jl_bkind_is_some_constant(decode_restriction_kind(pku))) { + enum jl_partition_kind kind = decode_restriction_kind(pku); + if (jl_bkind_is_some_constant(kind)) { if (!val) { break; } @@ -789,9 +790,24 @@ JL_DLLEXPORT jl_binding_partition_t *jl_declare_constant_val3( continue; } jl_gc_wb(bpart, val); - int needs_backdate = bpart->min_world == 0 && new_world && val; + size_t prev_min_world = bpart->min_world; bpart->min_world = new_world; - if (needs_backdate) { + int need_backdate = 0; + if (new_world && val) { + if (prev_min_world == 0) { + need_backdate = 1; + } else if (kind == BINDING_KIND_DECLARED) { + jl_binding_partition_t *prev_bpart = jl_get_binding_partition(b, prev_min_world-1); + jl_ptr_kind_union_t prev_pku = jl_atomic_load_relaxed(&prev_bpart->restriction); + if (prev_bpart->min_world == 0 && decode_restriction_kind(prev_pku) == BINDING_KIND_GUARD) { + // Just keep it simple and use one backdated const entry for both previous guard partition + // ranges. + jl_atomic_store_relaxed(&prev_bpart->max_world, new_world-1); + need_backdate = 1; + } + } + } + if (need_backdate) { jl_declare_constant_val3(b, mod, var, val, BINDING_KIND_BACKDATED_CONST, 0); } } @@ -1094,9 +1110,8 @@ JL_DLLEXPORT jl_value_t *jl_toplevel_eval_flex(jl_module_t *JL_NONNULL m, jl_val else { // use interpreter assert(thk); - if (has_opaque) { + if (has_opaque) jl_resolve_definition_effects_in_ir((jl_array_t*)thk->code, m, NULL, 0); - } size_t world = jl_atomic_load_acquire(&jl_world_counter); ct->world_age = world; result = jl_interpret_toplevel_thunk(m, thk); diff --git a/stdlib/REPL/src/REPL.jl b/stdlib/REPL/src/REPL.jl index f83fa867748af..699024c1723a4 100644 --- a/stdlib/REPL/src/REPL.jl +++ b/stdlib/REPL/src/REPL.jl @@ -44,8 +44,8 @@ function UndefVarError_hint(io::IO, ex::UndefVarError) "with the module it should come from.") elseif kind === Base.BINDING_KIND_GUARD print(io, "\nSuggestion: check for spelling errors or missing imports.") - else - print(io, "\nSuggestion: this global was defined as `$(bpart.restriction.globalref)` but not assigned a value.") + elseif Base.is_some_imported(kind) + print(io, "\nSuggestion: this global was defined as `$(Base.partition_restriction(bpart).globalref)` but not assigned a value.") end elseif scope === :static_parameter print(io, "\nSuggestion: run Test.detect_unbound_args to detect method arguments that do not fully constrain a type parameter.") @@ -1884,16 +1884,26 @@ function get_usings!(usings, ex) return usings end +function create_global_out!(mod) + if !isdefinedglobal(mod, :Out) + out = Dict{Int, Any}() + @eval mod begin + const Out = $(out) + export Out + end + return out + end + return getglobal(mod, Out) +end + function capture_result(n::Ref{Int}, @nospecialize(x)) n = n[] mod = Base.MainInclude - if !isdefined(mod, :Out) - @eval mod global Out - @eval mod export Out - setglobal!(mod, :Out, Dict{Int, Any}()) - end - if x !== getglobal(mod, :Out) && x !== nothing # remove this? - getglobal(mod, :Out)[n] = x + # TODO: This invokelatest is only required due to backdated constants + # and should be removed after + out = isdefinedglobal(mod, :Out) ? invokelatest(getglobal, mod, :Out) : invokelatest(create_global_out!, mod) + if x !== out && x !== nothing # remove this? + out[n] = x end nothing end diff --git a/stdlib/REPL/test/repl.jl b/stdlib/REPL/test/repl.jl index 8944fd76f31de..018cf9c36430e 100644 --- a/stdlib/REPL/test/repl.jl +++ b/stdlib/REPL/test/repl.jl @@ -752,11 +752,11 @@ fake_repl() do stdin_write, stdout_read, repl # Test removal of prefix in single statement paste sendrepl2("\e[200~julia> A = 2\e[201~\n") - @test Main.A == 2 + @test @world(Main.A, ∞) == 2 # Test removal of prefix in single statement paste sendrepl2("\e[200~In [12]: A = 2.2\e[201~\n") - @test Main.A == 2.2 + @test @world(Main.A, ∞) == 2.2 # Test removal of prefix in multiple statement paste sendrepl2("""\e[200~ @@ -768,7 +768,7 @@ fake_repl() do stdin_write, stdout_read, repl julia> A = 3\e[201~ """) - @test Main.A == 3 + @test @world(Main.A, ∞) == 3 @test @invokelatest(Main.foo(4)) @test @invokelatest(Main.T17599(3)).a == 3 @test !@invokelatest(Main.foo(2)) @@ -780,12 +780,12 @@ fake_repl() do stdin_write, stdout_read, repl julia> A = 4 4\e[201~ """) - @test Main.A == 4 + @test @world(Main.A, ∞) == 4 @test @invokelatest(Main.goo(4)) == 5 # Test prefix removal only active in bracket paste mode sendrepl2("julia = 4\n julia> 3 && (A = 1)\n") - @test Main.A == 1 + @test @world(Main.A, ∞) == 1 # Test that indentation corresponding to the prompt is removed s = sendrepl2("""\e[200~julia> begin\n α=1\n β=2\n end\n\e[201~""") @@ -820,8 +820,8 @@ fake_repl() do stdin_write, stdout_read, repl julia> B = 2 2\e[201~ """) - @test Main.A == 1 - @test Main.B == 2 + @test @world(Main.A, ∞) == 1 + @test @world(Main.B, ∞) == 2 end # redirect_stdout # Close repl diff --git a/sysimage.mk b/sysimage.mk index 571e2da003346..ae6ce8699f417 100644 --- a/sysimage.mk +++ b/sysimage.mk @@ -39,6 +39,7 @@ COMPILER_SRCS := $(addprefix $(JULIAHOME)/, \ base/error.jl \ base/essentials.jl \ base/expr.jl \ + base/exports.jl \ base/generator.jl \ base/int.jl \ base/indices.jl \ diff --git a/test/core.jl b/test/core.jl index 3886e6728df10..5e2677f0f075f 100644 --- a/test/core.jl +++ b/test/core.jl @@ -8233,6 +8233,7 @@ end let M = @__MODULE__ Core.eval(M, :(global a_typed_global)) @test Core.eval(M, :(global a_typed_global::$(Tuple{Union{Integer,Nothing}}))) === nothing + @Core.latestworld @test Core.get_binding_type(M, :a_typed_global) === Tuple{Union{Integer,Nothing}} @test Core.eval(M, :(global a_typed_global::$(Tuple{Union{Integer,Nothing}}))) === nothing @test Core.eval(M, :(global a_typed_global::$(Union{Tuple{Integer},Tuple{Nothing}}))) === nothing diff --git a/test/precompile.jl b/test/precompile.jl index f5a412b416ddc..a9516231ff8d7 100644 --- a/test/precompile.jl +++ b/test/precompile.jl @@ -1969,7 +1969,7 @@ precompile_test_harness("Issue #50538") do load_path """ module I50538 const newglobal = try - Base.newglobal = false + eval(Expr(:global, GlobalRef(Base, :newglobal))) catch ex ex isa ErrorException || rethrow() ex diff --git a/test/worlds.jl b/test/worlds.jl index 025aaba6cea4f..48fb6593d3a37 100644 --- a/test/worlds.jl +++ b/test/worlds.jl @@ -107,7 +107,7 @@ end g265() = [f265(x) for x in 1:3.] wc265 = get_world_counter() wc265_41332a = Task(tls_world_age) -@test tls_world_age() == wc265 +@test tls_world_age() == wc265 + 2 (function () global wc265_41332b = Task(tls_world_age) @eval f265(::Any) = 1.0 @@ -115,24 +115,24 @@ wc265_41332a = Task(tls_world_age) global wc265_41332d = Task(tls_world_age) nothing end)() -@test wc265 + 4 == get_world_counter() == tls_world_age() +@test wc265 + 10 == get_world_counter() == tls_world_age() schedule(wc265_41332a) schedule(wc265_41332b) schedule(wc265_41332c) schedule(wc265_41332d) -@test wc265 == fetch(wc265_41332a) -@test wc265 + 2 == fetch(wc265_41332b) -@test wc265 + 4 == fetch(wc265_41332c) -@test wc265 + 2 == fetch(wc265_41332d) +@test wc265 + 1 == fetch(wc265_41332a) +@test wc265 + 8 == fetch(wc265_41332b) +@test wc265 + 10 == fetch(wc265_41332c) +@test wc265 + 8 == fetch(wc265_41332d) chnls, tasks = Base.channeled_tasks(2, wfunc) t265 = tasks[1] wc265 = get_world_counter() @test put_n_take!(get_world_counter, ()) == wc265 -@test put_n_take!(tls_world_age, ()) == wc265 +@test put_n_take!(tls_world_age, ()) + 3 == wc265 f265(::Int) = 1 @test put_n_take!(get_world_counter, ()) == wc265 + 1 == get_world_counter() == tls_world_age() -@test put_n_take!(tls_world_age, ()) == wc265 +@test put_n_take!(tls_world_age, ()) + 3 == wc265 @test g265() == Int[1, 1, 1] @test Core.Compiler.return_type(f265, Tuple{Any,}) == Union{Float64, Int} @@ -162,12 +162,12 @@ let ex = t265.exception @test ex isa MethodError @test ex.f == h265 @test ex.args == () - @test ex.world == wc265 + @test ex.world == wc265-3 str = sprint(showerror, ex) wc = get_world_counter() cmps = """ MethodError: no method matching h265() - The applicable method may be too new: running in world age $wc265, while current world is $wc.""" + The applicable method may be too new: running in world age $(wc265-3), while current world is $wc.""" @test startswith(str, cmps) cmps = "\n h265() (method too new to be called from this world context.)\n $loc_h265" @test occursin(cmps, str) @@ -500,3 +500,12 @@ end end @test_throws ErrorException("Generated function result with `edges == nothing` and `max_world == typemax(UInt)` must have `min_world == 1`") generated_no_edges() + +# Test that backdating of constants is working for structs +before_backdate_age = Base.tls_world_age() +struct FooBackdated + x::Vector{FooBackdated} + + FooBackdated() = new(FooBackdated[]) +end +@test Base.invoke_in_world(before_backdate_age, isdefined, @__MODULE__, :FooBackdated) From e65af91a3d36c7635abb7284ee9b7c2e1aac89c3 Mon Sep 17 00:00:00 2001 From: DilumAluthgeBot <43731525+DilumAluthgeBot@users.noreply.github.com> Date: Wed, 29 Jan 2025 15:15:24 -0500 Subject: [PATCH 41/56] =?UTF-8?q?=F0=9F=A4=96=20[master]=20Bump=20the=20Pk?= =?UTF-8?q?g=20stdlib=20from=20bc9fb21b1=20to=206091533bc=20(#57194)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Stdlib: Pkg URL: https://github.com/JuliaLang/Pkg.jl.git Stdlib branch: master Julia branch: master Old commit: bc9fb21b1 New commit: 6091533bc Julia version: 1.12.0-DEV Pkg version: 1.12.0 Bump invoked by: @KristofferC Powered by: [BumpStdlibs.jl](https://github.com/JuliaLang/BumpStdlibs.jl) Diff: https://github.com/JuliaLang/Pkg.jl/compare/bc9fb21b1f2d72038491eff938673fc5fbc99445...6091533bc93c722591d3e7ee53d26012cb4d5aa4 ``` $ git log --oneline bc9fb21b1..6091533bc 6091533bc fix ambiguity in apps `rm` (#4144) ecdf6aa38 rename rot13 app in test to avoid name conflicts on systems where such a binary is already installed (#4143) a3626bf29 add `PREV_ENV_PATH` to globals getting reset and move the reset to before precompilation is finished instead of `__init__` (#4142) 938e9b24e app support in Pkg (#3772) df1931a93 Use copy_test_packages in some places, and ensure that the copied test package is user writable. This allows running the Pkg tests as part of Base.runtests of an installed Julia installation, where sources might be readonly. (#4136) 2609a9411 don't set up callbacks if using cli git (#4128) ``` Co-authored-by: KristofferC <1282691+KristofferC@users.noreply.github.com> --- .../Pkg-6091533bc93c722591d3e7ee53d26012cb4d5aa4.tar.gz/md5 | 1 + .../Pkg-6091533bc93c722591d3e7ee53d26012cb4d5aa4.tar.gz/sha512 | 1 + .../Pkg-bc9fb21b1f2d72038491eff938673fc5fbc99445.tar.gz/md5 | 1 - .../Pkg-bc9fb21b1f2d72038491eff938673fc5fbc99445.tar.gz/sha512 | 1 - stdlib/Pkg.version | 2 +- 5 files changed, 3 insertions(+), 3 deletions(-) create mode 100644 deps/checksums/Pkg-6091533bc93c722591d3e7ee53d26012cb4d5aa4.tar.gz/md5 create mode 100644 deps/checksums/Pkg-6091533bc93c722591d3e7ee53d26012cb4d5aa4.tar.gz/sha512 delete mode 100644 deps/checksums/Pkg-bc9fb21b1f2d72038491eff938673fc5fbc99445.tar.gz/md5 delete mode 100644 deps/checksums/Pkg-bc9fb21b1f2d72038491eff938673fc5fbc99445.tar.gz/sha512 diff --git a/deps/checksums/Pkg-6091533bc93c722591d3e7ee53d26012cb4d5aa4.tar.gz/md5 b/deps/checksums/Pkg-6091533bc93c722591d3e7ee53d26012cb4d5aa4.tar.gz/md5 new file mode 100644 index 0000000000000..b1012dffefbf6 --- /dev/null +++ b/deps/checksums/Pkg-6091533bc93c722591d3e7ee53d26012cb4d5aa4.tar.gz/md5 @@ -0,0 +1 @@ +26699bf323c07c7491d3c106219de6da diff --git a/deps/checksums/Pkg-6091533bc93c722591d3e7ee53d26012cb4d5aa4.tar.gz/sha512 b/deps/checksums/Pkg-6091533bc93c722591d3e7ee53d26012cb4d5aa4.tar.gz/sha512 new file mode 100644 index 0000000000000..2ed6fcd7c5f46 --- /dev/null +++ b/deps/checksums/Pkg-6091533bc93c722591d3e7ee53d26012cb4d5aa4.tar.gz/sha512 @@ -0,0 +1 @@ +1466dca4e49800b94d65ce59c3e2c75308545282a00720c964b34e0cde5e06aced80361c236a9ada81d35ebcc2692f8e6e84db507342d8055ba501fc1291c0a2 diff --git a/deps/checksums/Pkg-bc9fb21b1f2d72038491eff938673fc5fbc99445.tar.gz/md5 b/deps/checksums/Pkg-bc9fb21b1f2d72038491eff938673fc5fbc99445.tar.gz/md5 deleted file mode 100644 index 5180b5f916d1b..0000000000000 --- a/deps/checksums/Pkg-bc9fb21b1f2d72038491eff938673fc5fbc99445.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -2332986e216728bc85e364994f2ed910 diff --git a/deps/checksums/Pkg-bc9fb21b1f2d72038491eff938673fc5fbc99445.tar.gz/sha512 b/deps/checksums/Pkg-bc9fb21b1f2d72038491eff938673fc5fbc99445.tar.gz/sha512 deleted file mode 100644 index 04bc79171c734..0000000000000 --- a/deps/checksums/Pkg-bc9fb21b1f2d72038491eff938673fc5fbc99445.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -99bf03f921ae79767009dbd68a94a7119513b2454d5c9832b157bc1e092a35a6b90cb7a5d81346a7d927f9b0275328582098ca6b8b376b4a406ddf0b3167a280 diff --git a/stdlib/Pkg.version b/stdlib/Pkg.version index 4240c77105583..c5e38c1fe6487 100644 --- a/stdlib/Pkg.version +++ b/stdlib/Pkg.version @@ -1,4 +1,4 @@ PKG_BRANCH = master -PKG_SHA1 = bc9fb21b1f2d72038491eff938673fc5fbc99445 +PKG_SHA1 = 6091533bc93c722591d3e7ee53d26012cb4d5aa4 PKG_GIT_URL := https://github.com/JuliaLang/Pkg.jl.git PKG_TAR_URL = https://api.github.com/repos/JuliaLang/Pkg.jl/tarball/$1 From c172a646a33a7a500f7fbfff3475c4348bf5b376 Mon Sep 17 00:00:00 2001 From: Timothy Date: Thu, 30 Jan 2025 07:49:07 +0800 Subject: [PATCH 42/56] Change JuliaSyntaxHighlighting UUID (#56852) TLDR; I made a silly mistake a while back, and this seems like the least bad option. ----- Unfortunately during development of the backported version of this stdlib, to avoid potential sysimage issues the UUID was temporarilly changed and I forgot to change it back. This mistake lead to the backport/compat verison of the package having a different UUID from the in-tree stdlib. Beyond this being an embarassing mistake, it's also a pain because it means that we now have incorrect/invalid manifests. A small mercy is that 1.12 hasn't been released yet, so the discrepancy currently only affects people using development Julia versions. Changing the UUID here will require these people to regenerate their manifests, but this seems like the lesser of two evils. Appologies for the hassle all. Co-authored-by: Chengyu Han --- .../md5 | 1 - .../sha512 | 1 - .../md5 | 1 + .../sha512 | 1 + doc/Manifest.toml | 2 +- stdlib/JuliaSyntaxHighlighting.version | 2 +- stdlib/Manifest.toml | 2 +- stdlib/Markdown/Project.toml | 2 +- stdlib/Project.toml | 2 +- stdlib/REPL/Project.toml | 2 +- 10 files changed, 8 insertions(+), 8 deletions(-) delete mode 100644 deps/checksums/JuliaSyntaxHighlighting-19bd57b89c648592155156049addf67e0638eab1.tar.gz/md5 delete mode 100644 deps/checksums/JuliaSyntaxHighlighting-19bd57b89c648592155156049addf67e0638eab1.tar.gz/sha512 create mode 100644 deps/checksums/JuliaSyntaxHighlighting-eb8097c5f1dbfca80b5e6664af031ff1fe0904af.tar.gz/md5 create mode 100644 deps/checksums/JuliaSyntaxHighlighting-eb8097c5f1dbfca80b5e6664af031ff1fe0904af.tar.gz/sha512 diff --git a/deps/checksums/JuliaSyntaxHighlighting-19bd57b89c648592155156049addf67e0638eab1.tar.gz/md5 b/deps/checksums/JuliaSyntaxHighlighting-19bd57b89c648592155156049addf67e0638eab1.tar.gz/md5 deleted file mode 100644 index a86f3fe9c5561..0000000000000 --- a/deps/checksums/JuliaSyntaxHighlighting-19bd57b89c648592155156049addf67e0638eab1.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -401bb32ca43a8460d6790ee80e695bb5 diff --git a/deps/checksums/JuliaSyntaxHighlighting-19bd57b89c648592155156049addf67e0638eab1.tar.gz/sha512 b/deps/checksums/JuliaSyntaxHighlighting-19bd57b89c648592155156049addf67e0638eab1.tar.gz/sha512 deleted file mode 100644 index 6e54aef5fd34f..0000000000000 --- a/deps/checksums/JuliaSyntaxHighlighting-19bd57b89c648592155156049addf67e0638eab1.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -db2c732d3343f5a8770b3516cdd900587d497feab2259a937d354fac436ab3cb099b0401fb4e05817e75744fb9877ab69b1e4879d8a710b33b69c95b7e58d961 diff --git a/deps/checksums/JuliaSyntaxHighlighting-eb8097c5f1dbfca80b5e6664af031ff1fe0904af.tar.gz/md5 b/deps/checksums/JuliaSyntaxHighlighting-eb8097c5f1dbfca80b5e6664af031ff1fe0904af.tar.gz/md5 new file mode 100644 index 0000000000000..2b717077de398 --- /dev/null +++ b/deps/checksums/JuliaSyntaxHighlighting-eb8097c5f1dbfca80b5e6664af031ff1fe0904af.tar.gz/md5 @@ -0,0 +1 @@ +73d1db780528e93c4b756a2182bacfc2 diff --git a/deps/checksums/JuliaSyntaxHighlighting-eb8097c5f1dbfca80b5e6664af031ff1fe0904af.tar.gz/sha512 b/deps/checksums/JuliaSyntaxHighlighting-eb8097c5f1dbfca80b5e6664af031ff1fe0904af.tar.gz/sha512 new file mode 100644 index 0000000000000..dac11d46691a9 --- /dev/null +++ b/deps/checksums/JuliaSyntaxHighlighting-eb8097c5f1dbfca80b5e6664af031ff1fe0904af.tar.gz/sha512 @@ -0,0 +1 @@ +a994a3a9af8188f4c5985b91c8dc3e449b101eb4e56c4f9bccd69a528bd13fa919cc0cd39f101afa3d4245ad3b8e6691ca539969fefa0868e4b1f256d6ffa49e diff --git a/doc/Manifest.toml b/doc/Manifest.toml index e91958808828e..481b7240c0caf 100644 --- a/doc/Manifest.toml +++ b/doc/Manifest.toml @@ -101,7 +101,7 @@ version = "0.21.4" [[deps.JuliaSyntaxHighlighting]] deps = ["StyledStrings"] -uuid = "dc6e5ff7-fb65-4e79-a425-ec3bc9c03011" +uuid = "ac6e5ff7-fb65-4e79-a425-ec3bc9c03011" version = "1.12.0" [[deps.LazilyInitializedFields]] diff --git a/stdlib/JuliaSyntaxHighlighting.version b/stdlib/JuliaSyntaxHighlighting.version index 2a409c721d32b..ab3b0e930eb48 100644 --- a/stdlib/JuliaSyntaxHighlighting.version +++ b/stdlib/JuliaSyntaxHighlighting.version @@ -1,4 +1,4 @@ JULIASYNTAXHIGHLIGHTING_BRANCH = main -JULIASYNTAXHIGHLIGHTING_SHA1 = 19bd57b89c648592155156049addf67e0638eab1 +JULIASYNTAXHIGHLIGHTING_SHA1 = eb8097c5f1dbfca80b5e6664af031ff1fe0904af JULIASYNTAXHIGHLIGHTING_GIT_URL := https://github.com/julialang/JuliaSyntaxHighlighting.jl.git JULIASYNTAXHIGHLIGHTING_TAR_URL = https://api.github.com/repos/julialang/JuliaSyntaxHighlighting.jl/tarball/$1 diff --git a/stdlib/Manifest.toml b/stdlib/Manifest.toml index f029a210320cc..b149532d7b203 100644 --- a/stdlib/Manifest.toml +++ b/stdlib/Manifest.toml @@ -67,7 +67,7 @@ version = "1.11.0" [[deps.JuliaSyntaxHighlighting]] deps = ["StyledStrings"] -uuid = "dc6e5ff7-fb65-4e79-a425-ec3bc9c03011" +uuid = "ac6e5ff7-fb65-4e79-a425-ec3bc9c03011" version = "1.12.0" [[deps.LLD_jll]] diff --git a/stdlib/Markdown/Project.toml b/stdlib/Markdown/Project.toml index a48a3d1f0b345..1d5962c80803d 100644 --- a/stdlib/Markdown/Project.toml +++ b/stdlib/Markdown/Project.toml @@ -4,7 +4,7 @@ version = "1.11.0" [deps] Base64 = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" -JuliaSyntaxHighlighting = "dc6e5ff7-fb65-4e79-a425-ec3bc9c03011" +JuliaSyntaxHighlighting = "ac6e5ff7-fb65-4e79-a425-ec3bc9c03011" StyledStrings = "f489334b-da3d-4c2e-b8f0-e476e12c162b" [extras] diff --git a/stdlib/Project.toml b/stdlib/Project.toml index 92996cf017d0d..1e03a0f474490 100644 --- a/stdlib/Project.toml +++ b/stdlib/Project.toml @@ -12,7 +12,7 @@ FileWatching = "7b1f6079-737a-58dc-b8bc-7a2ca5c1b5ee" Future = "9fa8497b-333b-5362-9e8d-4d0656e87820" GMP_jll = "781609d7-10c4-51f6-84f2-b8444358ff6d" InteractiveUtils = "b77e0a4c-d291-57a0-90e8-8db25a27a240" -JuliaSyntaxHighlighting = "dc6e5ff7-fb65-4e79-a425-ec3bc9c03011" +JuliaSyntaxHighlighting = "ac6e5ff7-fb65-4e79-a425-ec3bc9c03011" LLD_jll = "d55e3150-da41-5e91-b323-ecfd1eec6109" LLVMLibUnwind_jll = "47c5dbc3-30ba-59ef-96a6-123e260183d9" LazyArtifacts = "4af54fe1-eca0-43a8-85a7-787d91b784e3" diff --git a/stdlib/REPL/Project.toml b/stdlib/REPL/Project.toml index f60a6a4766093..5ee3849f507f2 100644 --- a/stdlib/REPL/Project.toml +++ b/stdlib/REPL/Project.toml @@ -4,7 +4,7 @@ version = "1.11.0" [deps] InteractiveUtils = "b77e0a4c-d291-57a0-90e8-8db25a27a240" -JuliaSyntaxHighlighting = "dc6e5ff7-fb65-4e79-a425-ec3bc9c03011" +JuliaSyntaxHighlighting = "ac6e5ff7-fb65-4e79-a425-ec3bc9c03011" Markdown = "d6f4376e-aef5-505a-96c1-9c027394607a" Sockets = "6462fe0b-24de-5631-8697-dd941f90decc" StyledStrings = "f489334b-da3d-4c2e-b8f0-e476e12c162b" From 683c5e7ec4977f3c77e29fb47a13cc4c78cdf595 Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Thu, 30 Jan 2025 00:28:48 -0500 Subject: [PATCH 43/56] move forward edges stripping code to correct place (#57197) Fixes a regression in the size of stripped binaries. --- src/staticdata.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/staticdata.c b/src/staticdata.c index ff352cd8c152f..b5d6fb7cdd62a 100644 --- a/src/staticdata.c +++ b/src/staticdata.c @@ -2623,7 +2623,6 @@ static void strip_specializations_(jl_method_instance_t *mi) if (inferred && inferred != jl_nothing) { if (jl_options.strip_ir) { record_field_change((jl_value_t**)&codeinst->inferred, jl_nothing); - record_field_change((jl_value_t**)&codeinst->edges, (jl_value_t*)jl_emptysvec); } else if (jl_options.strip_metadata) { jl_value_t *stripped = strip_codeinfo_meta(mi->def.method, inferred, codeinst); @@ -2632,6 +2631,8 @@ static void strip_specializations_(jl_method_instance_t *mi) } } } + if (jl_options.strip_ir) + record_field_change((jl_value_t**)&codeinst->edges, (jl_value_t*)jl_emptysvec); if (jl_options.strip_metadata) record_field_change((jl_value_t**)&codeinst->debuginfo, (jl_value_t*)jl_nulldebuginfo); codeinst = jl_atomic_load_relaxed(&codeinst->next); From 9777a33824c1f954dc334ad3de7618b173998eb6 Mon Sep 17 00:00:00 2001 From: Cody Tapscott <84105208+topolarity@users.noreply.github.com> Date: Thu, 30 Jan 2025 07:55:51 -0500 Subject: [PATCH 44/56] codegen (stubs): report success when registering `@ccallable` (#57200) This is required to be able to load a sysimage with ccallable entrypoints without `libjulia-codegen.so` --- src/codegen-stubs.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/codegen-stubs.c b/src/codegen-stubs.c index 5e243ddda28c9..6b547251eaab8 100644 --- a/src/codegen-stubs.c +++ b/src/codegen-stubs.c @@ -73,7 +73,10 @@ JL_DLLEXPORT uint32_t jl_get_LLVM_VERSION_fallback(void) JL_DLLEXPORT int jl_compile_extern_c_fallback(LLVMOrcThreadSafeModuleRef llvmmod, void *params, void *sysimg, jl_value_t *declrt, jl_value_t *sigt) { - return 0; + // Assume we were able to register the ccallable with the JIT. The + // fact that we didn't is not observable since we cannot compile + // anything else. + return 1; } JL_DLLEXPORT void jl_teardown_codegen_fallback(void) JL_NOTSAFEPOINT From 3e08cb1b99e28bc2131bfbb840c5c23619c0bf15 Mon Sep 17 00:00:00 2001 From: Chengyu Han Date: Fri, 31 Jan 2025 00:00:46 +0800 Subject: [PATCH 45/56] deps: update JuliaSyntax to v1.0.0 (#57188) This pr: - Update JuliaSyntax to v1.0.0 - Update JuliaSyntaxHighlighting to latest Note: `JuliaSyntax.jl` contains some breaking changes in v1.0.0, so we need to update `JuliaSyntaxHighlighting.jl` at the same time. Close #57166 --- deps/JuliaSyntax.version | 2 +- .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 - .../sha512 | 1 - .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 - .../sha512 | 1 - stdlib/JuliaSyntaxHighlighting.version | 2 +- test/syntax.jl | 16 +++++++++------- 11 files changed, 15 insertions(+), 13 deletions(-) create mode 100644 deps/checksums/JuliaSyntax-2e965a159dd9f87d216d2d50ecbd2ed4f9af2c5a.tar.gz/md5 create mode 100644 deps/checksums/JuliaSyntax-2e965a159dd9f87d216d2d50ecbd2ed4f9af2c5a.tar.gz/sha512 delete mode 100644 deps/checksums/JuliaSyntax-dfd1d69b153eb119873035e62993a109b27192f0.tar.gz/md5 delete mode 100644 deps/checksums/JuliaSyntax-dfd1d69b153eb119873035e62993a109b27192f0.tar.gz/sha512 create mode 100644 deps/checksums/JuliaSyntaxHighlighting-2680c8bde1aa274f25d7a434c645f16b3a1ee731.tar.gz/md5 create mode 100644 deps/checksums/JuliaSyntaxHighlighting-2680c8bde1aa274f25d7a434c645f16b3a1ee731.tar.gz/sha512 delete mode 100644 deps/checksums/JuliaSyntaxHighlighting-eb8097c5f1dbfca80b5e6664af031ff1fe0904af.tar.gz/md5 delete mode 100644 deps/checksums/JuliaSyntaxHighlighting-eb8097c5f1dbfca80b5e6664af031ff1fe0904af.tar.gz/sha512 diff --git a/deps/JuliaSyntax.version b/deps/JuliaSyntax.version index 86f94135884a0..a7d31b7c16403 100644 --- a/deps/JuliaSyntax.version +++ b/deps/JuliaSyntax.version @@ -1,4 +1,4 @@ JULIASYNTAX_BRANCH = main -JULIASYNTAX_SHA1 = dfd1d69b153eb119873035e62993a109b27192f0 +JULIASYNTAX_SHA1 = 2e965a159dd9f87d216d2d50ecbd2ed4f9af2c5a JULIASYNTAX_GIT_URL := https://github.com/JuliaLang/JuliaSyntax.jl.git JULIASYNTAX_TAR_URL = https://api.github.com/repos/JuliaLang/JuliaSyntax.jl/tarball/$1 diff --git a/deps/checksums/JuliaSyntax-2e965a159dd9f87d216d2d50ecbd2ed4f9af2c5a.tar.gz/md5 b/deps/checksums/JuliaSyntax-2e965a159dd9f87d216d2d50ecbd2ed4f9af2c5a.tar.gz/md5 new file mode 100644 index 0000000000000..96f356f3faaec --- /dev/null +++ b/deps/checksums/JuliaSyntax-2e965a159dd9f87d216d2d50ecbd2ed4f9af2c5a.tar.gz/md5 @@ -0,0 +1 @@ +40d7bcc6e5741d50a457ace2ca8b2c0c diff --git a/deps/checksums/JuliaSyntax-2e965a159dd9f87d216d2d50ecbd2ed4f9af2c5a.tar.gz/sha512 b/deps/checksums/JuliaSyntax-2e965a159dd9f87d216d2d50ecbd2ed4f9af2c5a.tar.gz/sha512 new file mode 100644 index 0000000000000..fd7770cdeaa75 --- /dev/null +++ b/deps/checksums/JuliaSyntax-2e965a159dd9f87d216d2d50ecbd2ed4f9af2c5a.tar.gz/sha512 @@ -0,0 +1 @@ +b9429b90a28460ef0272cd42a5c221629c6d60221ed088ae3e591cc3d8dbdec32788074397419e58b611bda7df32c7379ec7fafeead7056ed9665591474cec5d diff --git a/deps/checksums/JuliaSyntax-dfd1d69b153eb119873035e62993a109b27192f0.tar.gz/md5 b/deps/checksums/JuliaSyntax-dfd1d69b153eb119873035e62993a109b27192f0.tar.gz/md5 deleted file mode 100644 index 51b30461d3905..0000000000000 --- a/deps/checksums/JuliaSyntax-dfd1d69b153eb119873035e62993a109b27192f0.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -e58559668aabb0fa96d598970c4d648e diff --git a/deps/checksums/JuliaSyntax-dfd1d69b153eb119873035e62993a109b27192f0.tar.gz/sha512 b/deps/checksums/JuliaSyntax-dfd1d69b153eb119873035e62993a109b27192f0.tar.gz/sha512 deleted file mode 100644 index 63a513ec9ae63..0000000000000 --- a/deps/checksums/JuliaSyntax-dfd1d69b153eb119873035e62993a109b27192f0.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -59e22f7db63a383beadf96a68d4db6ae173d61be6d766ea1792b3a3bd70125f73dd4df9e55bad4c66363aa0b6ff6ea5259d3c91abf42f5fe34446e3fa076cc87 diff --git a/deps/checksums/JuliaSyntaxHighlighting-2680c8bde1aa274f25d7a434c645f16b3a1ee731.tar.gz/md5 b/deps/checksums/JuliaSyntaxHighlighting-2680c8bde1aa274f25d7a434c645f16b3a1ee731.tar.gz/md5 new file mode 100644 index 0000000000000..30284ccf352d4 --- /dev/null +++ b/deps/checksums/JuliaSyntaxHighlighting-2680c8bde1aa274f25d7a434c645f16b3a1ee731.tar.gz/md5 @@ -0,0 +1 @@ +187f155c32a79f57a89e31e672d2d8c5 diff --git a/deps/checksums/JuliaSyntaxHighlighting-2680c8bde1aa274f25d7a434c645f16b3a1ee731.tar.gz/sha512 b/deps/checksums/JuliaSyntaxHighlighting-2680c8bde1aa274f25d7a434c645f16b3a1ee731.tar.gz/sha512 new file mode 100644 index 0000000000000..bdce410b84d69 --- /dev/null +++ b/deps/checksums/JuliaSyntaxHighlighting-2680c8bde1aa274f25d7a434c645f16b3a1ee731.tar.gz/sha512 @@ -0,0 +1 @@ +69347af996d77b88b5e5b6e44ff046e9197775a66802a0da6fb5fcbf9e5ca533566955c8435bc25490f6ca0c002b4c1effcddaf932b7eb91e00a8f99554b7b8d diff --git a/deps/checksums/JuliaSyntaxHighlighting-eb8097c5f1dbfca80b5e6664af031ff1fe0904af.tar.gz/md5 b/deps/checksums/JuliaSyntaxHighlighting-eb8097c5f1dbfca80b5e6664af031ff1fe0904af.tar.gz/md5 deleted file mode 100644 index 2b717077de398..0000000000000 --- a/deps/checksums/JuliaSyntaxHighlighting-eb8097c5f1dbfca80b5e6664af031ff1fe0904af.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -73d1db780528e93c4b756a2182bacfc2 diff --git a/deps/checksums/JuliaSyntaxHighlighting-eb8097c5f1dbfca80b5e6664af031ff1fe0904af.tar.gz/sha512 b/deps/checksums/JuliaSyntaxHighlighting-eb8097c5f1dbfca80b5e6664af031ff1fe0904af.tar.gz/sha512 deleted file mode 100644 index dac11d46691a9..0000000000000 --- a/deps/checksums/JuliaSyntaxHighlighting-eb8097c5f1dbfca80b5e6664af031ff1fe0904af.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -a994a3a9af8188f4c5985b91c8dc3e449b101eb4e56c4f9bccd69a528bd13fa919cc0cd39f101afa3d4245ad3b8e6691ca539969fefa0868e4b1f256d6ffa49e diff --git a/stdlib/JuliaSyntaxHighlighting.version b/stdlib/JuliaSyntaxHighlighting.version index ab3b0e930eb48..14eb1cedf49a4 100644 --- a/stdlib/JuliaSyntaxHighlighting.version +++ b/stdlib/JuliaSyntaxHighlighting.version @@ -1,4 +1,4 @@ JULIASYNTAXHIGHLIGHTING_BRANCH = main -JULIASYNTAXHIGHLIGHTING_SHA1 = eb8097c5f1dbfca80b5e6664af031ff1fe0904af +JULIASYNTAXHIGHLIGHTING_SHA1 = 2680c8bde1aa274f25d7a434c645f16b3a1ee731 JULIASYNTAXHIGHLIGHTING_GIT_URL := https://github.com/julialang/JuliaSyntaxHighlighting.jl.git JULIASYNTAXHIGHLIGHTING_TAR_URL = https://api.github.com/repos/julialang/JuliaSyntaxHighlighting.jl/tarball/$1 diff --git a/test/syntax.jl b/test/syntax.jl index aaeeea7aec161..f29dd4978d309 100644 --- a/test/syntax.jl +++ b/test/syntax.jl @@ -377,8 +377,8 @@ add_method_to_glob_fn!() @test_parseerror "function finally() end" # PR #16170 -@test Meta.lower(Main, Meta.parse("true(x) = x")) == Expr(:error, "invalid function name \"true\"") -@test Meta.lower(Main, Meta.parse("false(x) = x")) == Expr(:error, "invalid function name \"false\"") +@test Meta.lower(Main, Meta.parse("true(x) = x")) == Expr(:error, "\"true\" is not a valid function argument name") +@test Meta.lower(Main, Meta.parse("false(x) = x")) == Expr(:error, "\"false\" is not a valid function argument name") # issue #16355 @test Meta.lower(Main, :(f(d:Int...) = nothing)) == Expr(:error, "\"d:Int\" is not a valid function argument name") @@ -925,8 +925,8 @@ g21054(>:) = >:2 @test g21054(-) == -2 # issue #21168 -@test Meta.lower(Main, :(a.[1])) == Expr(:error, "invalid syntax \"a.[1]\"") -@test Meta.lower(Main, :(a.{1})) == Expr(:error, "invalid syntax \"a.{1}\"") +@test_broken Meta.lower(Main, :(a.[1])) == Expr(:error, "invalid syntax \"a.[1]\"") +@test_broken Meta.lower(Main, :(a.{1})) == Expr(:error, "invalid syntax \"a.{1}\"") # Issue #21225 let abstr = Meta.parse("abstract type X end") @@ -1496,8 +1496,8 @@ end # issue #26739 let exc = try Core.eval(@__MODULE__, :(sin.[1])) catch exc ; exc end - @test exc isa ErrorException - @test startswith(exc.msg, "syntax: invalid syntax \"sin.[1]\"") + @test_broken exc isa ErrorException + @test_broken startswith(exc.msg, "syntax: invalid syntax \"sin.[1]\"") end # issue #26873 @@ -2454,7 +2454,9 @@ end @test_throws MethodError @m37134()(1.0) == 62 macro n37134() - :($(esc(Expr(:tuple, Expr(:..., :x))))->$(esc(:x))) + quote + ((x...,)) -> (x) + end |> esc end @test @n37134()(2,1) === (2,1) From cf4ab8336468f73bbc768187864586bedbd59342 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Thu, 30 Jan 2025 16:15:48 -0500 Subject: [PATCH 46/56] REPL: fix test that fails locally via `Pkg.test` (#57202) Via `Base.runtests` it seems stdlibs are generally available in test environments. In `Pkg.test` they must be in the test env, which Profile isn't, so this failed locally. All REPL tests pass locally with this. --- stdlib/REPL/test/docview.jl | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/stdlib/REPL/test/docview.jl b/stdlib/REPL/test/docview.jl index c81715ad69921..8c92d9b3b1d95 100644 --- a/stdlib/REPL/test/docview.jl +++ b/stdlib/REPL/test/docview.jl @@ -29,8 +29,22 @@ end end @testset "non-loaded packages in doc search" begin - str = get_help_io("Profile") - @test occursin("Couldn't find Profile, but a loadable package with that name exists.", str) + temp_package = mktempdir() + write(joinpath(temp_package, "Project.toml"), + """ + name = "FooPackage" + uuid = "2e6e0b2d-0e7f-4b7f-9f3b-6f3f3f3f3f3f" + """) + mkpath(joinpath(temp_package, "src")) + write(joinpath(temp_package, "src", "FooPackage.jl"), + """ + module FooPackage + end + """) + push!(LOAD_PATH, temp_package) + str = get_help_io("FooPackage") + @test occursin("Couldn't find FooPackage, but a loadable package with that name exists.", str) + @test pop!(LOAD_PATH) == temp_package end @testset "Check @var_str also completes to var\"\" in REPL.doc_completions()" begin From 2a9c1336d7c89955f60cf6619810ed4150d70ac9 Mon Sep 17 00:00:00 2001 From: Cody Tapscott <84105208+topolarity@users.noreply.github.com> Date: Fri, 31 Jan 2025 06:57:36 -0500 Subject: [PATCH 47/56] juliac: add pre-compilation step to build pipeline (#57207) This is required to avoid obscure loading / package errors during the trimming build process. --- contrib/juliac.jl | 95 +++++++++++++++++++++++++++++------------------ 1 file changed, 58 insertions(+), 37 deletions(-) diff --git a/contrib/juliac.jl b/contrib/juliac.jl index 20d56615c6357..3d1a3ef13fe79 100644 --- a/contrib/juliac.jl +++ b/contrib/juliac.jl @@ -1,8 +1,7 @@ # Julia compiler wrapper script # NOTE: The interface and location of this script are considered unstable/experimental -cmd = Base.julia_cmd() -cmd = `$cmd --startup-file=no --history-file=no` +julia_cmd = `$(Base.julia_cmd()) --startup-file=no --history-file=no` output_type = nothing # exe, sharedlib, sysimage outname = nothing file = nothing @@ -55,9 +54,9 @@ isnothing(outname) && error("No output file specified") isnothing(file) && error("No input file specified") absfile = abspath(file) -cflags = readchomp(`$(cmd) $(joinpath(Sys.BINDIR, Base.DATAROOTDIR,"julia", "julia-config.jl")) --cflags `) +cflags = readchomp(`$(julia_cmd) $(joinpath(Sys.BINDIR, Base.DATAROOTDIR,"julia", "julia-config.jl")) --cflags `) cflags = Base.shell_split(cflags) -allflags = readchomp(`$(cmd) $(joinpath(Sys.BINDIR, Base.DATAROOTDIR,"julia", "julia-config.jl")) --allflags`) +allflags = readchomp(`$(julia_cmd) $(joinpath(Sys.BINDIR, Base.DATAROOTDIR,"julia", "julia-config.jl")) --allflags`) allflags = Base.shell_split(allflags) tmpdir = mktempdir(cleanup=false) initsrc_path = joinpath(tmpdir, "init.c") @@ -65,45 +64,67 @@ init_path = joinpath(tmpdir, "init.a") img_path = joinpath(tmpdir, "img.a") bc_path = joinpath(tmpdir, "img-bc.a") -open(initsrc_path, "w") do io - print(io, """ - #include - __attribute__((constructor)) void static_init(void) { - if (jl_is_initialized()) - return; - julia_init(JL_IMAGE_IN_MEMORY); - jl_exception_clear(); - } - """) -end -cmd = addenv(`$cmd --project=$(Base.active_project()) --output-o $img_path --output-incremental=no --strip-ir --strip-metadata $julia_args $(joinpath(@__DIR__,"juliac-buildscript.jl")) $absfile $output_type $add_ccallables`, "OPENBLAS_NUM_THREADS" => 1, "JULIA_NUM_THREADS" => 1) -verbose && println("Running: $cmd") -if !success(pipeline(cmd; stdout, stderr)) - println(stderr, "\nFailed to compile $file") - exit(1) +function precompile_env() + # Pre-compile the environment + # (otherwise obscure error messages will occur) + cmd = addenv(`$julia_cmd --project=$(Base.active_project()) -e "using Pkg; Pkg.precompile()"`) + verbose && println("Running: $cmd") + if !success(pipeline(cmd; stdout, stderr)) + println(stderr, "\nError encountered during pre-compilation of environment.") + exit(1) + end end -run(`cc $(cflags) -g -c -o $init_path $initsrc_path`) +function compile_products() + # Compile the Julia code + cmd = addenv(`$julia_cmd --project=$(Base.active_project()) --output-o $img_path --output-incremental=no --strip-ir --strip-metadata $julia_args $(joinpath(@__DIR__,"juliac-buildscript.jl")) $absfile $output_type $add_ccallables`, "OPENBLAS_NUM_THREADS" => 1, "JULIA_NUM_THREADS" => 1) + verbose && println("Running: $cmd") + if !success(pipeline(cmd; stdout, stderr)) + println(stderr, "\nFailed to compile $file") + exit(1) + end -if output_type == "--output-lib" || output_type == "--output-sysimage" - of, ext = splitext(outname) - soext = "." * Base.BinaryPlatforms.platform_dlext() - if ext == "" - outname = of * soext + # Compile the initialization code + open(initsrc_path, "w") do io + print(io, """ + #include + __attribute__((constructor)) void static_init(void) { + if (jl_is_initialized()) + return; + julia_init(JL_IMAGE_IN_MEMORY); + jl_exception_clear(); + } + """) end + run(`cc $(cflags) -g -c -o $init_path $initsrc_path`) end -julia_libs = Base.shell_split(Base.isdebugbuild() ? "-ljulia-debug -ljulia-internal-debug" : "-ljulia -ljulia-internal") -try - if output_type == "--output-lib" - run(`cc $(allflags) -o $outname -shared -Wl,$(Base.Linking.WHOLE_ARCHIVE) $img_path -Wl,$(Base.Linking.NO_WHOLE_ARCHIVE) $init_path $(julia_libs)`) - elseif output_type == "--output-sysimage" - run(`cc $(allflags) -o $outname -shared -Wl,$(Base.Linking.WHOLE_ARCHIVE) $img_path -Wl,$(Base.Linking.NO_WHOLE_ARCHIVE) $(julia_libs)`) - else - run(`cc $(allflags) -o $outname -Wl,$(Base.Linking.WHOLE_ARCHIVE) $img_path -Wl,$(Base.Linking.NO_WHOLE_ARCHIVE) $init_path $(julia_libs)`) +function link_products() + global outname + if output_type == "--output-lib" || output_type == "--output-sysimage" + of, ext = splitext(outname) + soext = "." * Base.BinaryPlatforms.platform_dlext() + if ext == "" + outname = of * soext + end + end + + julia_libs = Base.shell_split(Base.isdebugbuild() ? "-ljulia-debug -ljulia-internal-debug" : "-ljulia -ljulia-internal") + try + if output_type == "--output-lib" + run(`cc $(allflags) -o $outname -shared -Wl,$(Base.Linking.WHOLE_ARCHIVE) $img_path -Wl,$(Base.Linking.NO_WHOLE_ARCHIVE) $init_path $(julia_libs)`) + elseif output_type == "--output-sysimage" + run(`cc $(allflags) -o $outname -shared -Wl,$(Base.Linking.WHOLE_ARCHIVE) $img_path -Wl,$(Base.Linking.NO_WHOLE_ARCHIVE) $(julia_libs)`) + else + run(`cc $(allflags) -o $outname -Wl,$(Base.Linking.WHOLE_ARCHIVE) $img_path -Wl,$(Base.Linking.NO_WHOLE_ARCHIVE) $init_path $(julia_libs)`) + end + catch e + println("\nCompilation failed: ", e) + exit(1) end -catch - println("\nCompilation failed.") - exit(1) end + +precompile_env() +compile_products() +link_products() From 162cfb3b46d95c96965b0d8df41f1f65ff560840 Mon Sep 17 00:00:00 2001 From: Cody Tapscott <84105208+topolarity@users.noreply.github.com> Date: Fri, 31 Jan 2025 07:00:43 -0500 Subject: [PATCH 48/56] juliac: add `invokelatest_gr` override to juliac-buildscript.jl (#57205) This extends the existing `invokelatest` hack to treat the current world as the latest world for globalref lookups. Since this came up, @Keno can you confirm that any world-age-affecting operations on bindings still require `eval()`? Are there any imports / assignments / etc. that might bump the world age by changing a binding? --- contrib/juliac-buildscript.jl | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/contrib/juliac-buildscript.jl b/contrib/juliac-buildscript.jl index 0303e95f448b5..c23b679272b1e 100644 --- a/contrib/juliac-buildscript.jl +++ b/contrib/juliac-buildscript.jl @@ -38,6 +38,14 @@ end @inline function invokelatest(f::F, args...; kwargs...) where F return f(args...; kwargs...) end + @inline function invokelatest_gr(gr::GlobalRef, @nospecialize args...; kwargs...) + @inline + kwargs = merge(NamedTuple(), kwargs) + if isempty(kwargs) + return apply_gr(gr, args...) + end + return apply_gr_kw(kwargs, gr, args...) + end function sprint(f::F, args::Vararg{Any,N}; context=nothing, sizehint::Integer=0) where {F<:Function,N} s = IOBuffer(sizehint=sizehint) if context isa Tuple From dbcdf735ff9ca016ad07725381bcfbc352fdac1b Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Fri, 31 Jan 2025 08:03:56 -0500 Subject: [PATCH 49/56] add interactive pool size to `Base.runtests` report (#57215) --- test/runtests.jl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/runtests.jl b/test/runtests.jl index f0c5e1b94c376..1872c888333da 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -120,7 +120,8 @@ cd(@__DIR__) do Running parallel tests with: getpid() = $(getpid()) nworkers() = $(nworkers()) - nthreads() = $(Threads.threadpoolsize()) + nthreads(:interactive) = $(Threads.threadpoolsize(:interactive)) + nthreads(:default) = $(Threads.threadpoolsize(:default)) Sys.CPU_THREADS = $(Sys.CPU_THREADS) Sys.total_memory() = $(Base.format_bytes(Sys.total_memory())) Sys.free_memory() = $(Base.format_bytes(Sys.free_memory())) From 3952c2cc8ebbd0513e3c53844116d77755036608 Mon Sep 17 00:00:00 2001 From: Jakob Nybo Nissen Date: Fri, 31 Jan 2025 14:18:48 +0100 Subject: [PATCH 50/56] Add `@inline` to Base.reinterpret (#57078) Reinterpret contains quite a bit of code to handle padding, and so doesn't usually inline for structs. However, it frequently compiles down to a noop, making an inline unusually valuable. --- base/essentials.jl | 1 + base/reinterpretarray.jl | 39 ++++++++++++++++++++++++--------------- 2 files changed, 25 insertions(+), 15 deletions(-) diff --git a/base/essentials.jl b/base/essentials.jl index 58e4ce1125093..5db7a5f6fb0d9 100644 --- a/base/essentials.jl +++ b/base/essentials.jl @@ -726,6 +726,7 @@ julia> reinterpret(Tuple{UInt16, UInt8}, (0x01, 0x0203)) """ function reinterpret(::Type{Out}, x) where {Out} + @inline if isprimitivetype(Out) && isprimitivetype(typeof(x)) return bitcast(Out, x) end diff --git a/base/reinterpretarray.jl b/base/reinterpretarray.jl index d31f3ebb5dd2d..4feb14539f9dc 100644 --- a/base/reinterpretarray.jl +++ b/base/reinterpretarray.jl @@ -871,25 +871,34 @@ end return out[] else # mismatched padding - GC.@preserve in out begin - ptr_in = unsafe_convert(Ptr{In}, in) - ptr_out = unsafe_convert(Ptr{Out}, out) + return _reinterpret_padding(Out, x) + end +end - if fieldcount(In) > 0 && ispacked(Out) - _copytopacked!(ptr_out, ptr_in) - elseif fieldcount(Out) > 0 && ispacked(In) - _copyfrompacked!(ptr_out, ptr_in) - else - packed = Ref{NTuple{inpackedsize, UInt8}}() - GC.@preserve packed begin - ptr_packed = unsafe_convert(Ptr{NTuple{inpackedsize, UInt8}}, packed) - _copytopacked!(ptr_packed, ptr_in) - _copyfrompacked!(ptr_out, ptr_packed) - end +# If the code reaches this part, it needs to handle padding and is unlikely +# to compile to a noop. Therefore, we don't forcibly inline it. +function _reinterpret_padding(::Type{Out}, x::In) where {Out, In} + inpackedsize = packedsize(In) + in = Ref{In}(x) + out = Ref{Out}() + GC.@preserve in out begin + ptr_in = unsafe_convert(Ptr{In}, in) + ptr_out = unsafe_convert(Ptr{Out}, out) + + if fieldcount(In) > 0 && ispacked(Out) + _copytopacked!(ptr_out, ptr_in) + elseif fieldcount(Out) > 0 && ispacked(In) + _copyfrompacked!(ptr_out, ptr_in) + else + packed = Ref{NTuple{inpackedsize, UInt8}}() + GC.@preserve packed begin + ptr_packed = unsafe_convert(Ptr{NTuple{inpackedsize, UInt8}}, packed) + _copytopacked!(ptr_packed, ptr_in) + _copyfrompacked!(ptr_out, ptr_packed) end end - return out[] end + return out[] end From dbe19e49d9e7770856cdf88e492f22ec7827820d Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Fri, 31 Jan 2025 09:36:24 -0500 Subject: [PATCH 51/56] staticdata: Insert backedges recursively (#57212) In the new edges scheme, when we perform constant propagation, we create a dummy CodeInstance that is not inserted into the specializations cache, but instead simply serves as a container for all edges that were encountered during the constant propagation. These CodeInstances are not part of either the internal or external edges list collected during pkgimage generation. As such, while we were verifying edges recursively and would thus catch an invalidation prior to package image reload, we were failing to also insert backedges for these CodeInstance. We were thus failing to invalidate such methods if the method was redefined after re-load of the package image. Fix that by moving the storing of the backedges to the end of the validation code, so that it too happens recursively. --- base/staticdata.jl | 39 +++++++++++++++++---------------------- test/precompile.jl | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 22 deletions(-) diff --git a/base/staticdata.jl b/base/staticdata.jl index 79d81788cc16a..345769e479380 100644 --- a/base/staticdata.jl +++ b/base/staticdata.jl @@ -39,26 +39,16 @@ function _insert_backedges(edges::Vector{Any}, stack::Vector{CodeInstance}, visi verify_method_graph(codeinst, stack, visiting) minvalid = codeinst.min_world maxvalid = codeinst.max_world - if maxvalid ≥ minvalid - if get_world_counter() == maxvalid - # if this callee is still valid, add all the backedges - Base.Compiler.store_backedges(codeinst, codeinst.edges) - end - if get_world_counter() == maxvalid - maxvalid = typemax(UInt) - @atomic :monotonic codeinst.max_world = maxvalid - end - if external - caller = get_ci_mi(codeinst) - @assert isdefined(codeinst, :inferred) # See #53586, #53109 - inferred = @ccall jl_rettype_inferred( - codeinst.owner::Any, caller::Any, minvalid::UInt, maxvalid::UInt)::Any - if inferred !== nothing - # We already got a code instance for this world age range from - # somewhere else - we don't need this one. - else - @ccall jl_mi_cache_insert(caller::Any, codeinst::Any)::Cvoid - end + if maxvalid ≥ minvalid && external + caller = get_ci_mi(codeinst) + @assert isdefined(codeinst, :inferred) # See #53586, #53109 + inferred = @ccall jl_rettype_inferred( + codeinst.owner::Any, caller::Any, minvalid::UInt, maxvalid::UInt)::Any + if inferred !== nothing + # We already got a code instance for this world age range from + # somewhere else - we don't need this one. + else + @ccall jl_mi_cache_insert(caller::Any, codeinst::Any)::Cvoid end end end @@ -196,9 +186,14 @@ function verify_method(codeinst::CodeInstance, stack::Vector{CodeInstance}, visi while length(stack) ≥ depth child = pop!(stack) if maxworld ≠ 0 - @atomic :monotonic child.min_world = minworld + @atomic :monotonic child.min_world = minworld + end + if maxworld == current_world + Base.Compiler.store_backedges(child, child.edges) + @atomic :monotonic child.max_world = typemax(UInt) + else + @atomic :monotonic child.max_world = maxworld end - @atomic :monotonic child.max_world = maxworld @assert visiting[child] == length(stack) + 1 delete!(visiting, child) invalidations = _jl_debug_method_invalidation[] diff --git a/test/precompile.jl b/test/precompile.jl index a9516231ff8d7..194f88719642e 100644 --- a/test/precompile.jl +++ b/test/precompile.jl @@ -2222,4 +2222,37 @@ precompile_test_harness("No package module") do load_path String(take!(io))) end +precompile_test_harness("Constprop CodeInstance invalidation") do load_path + write(joinpath(load_path, "DefineTheMethod.jl"), + """ + module DefineTheMethod + export the_method + the_method_val(::Val{x}) where {x} = x + the_method_val(::Val{1}) = 0xdeadbeef + the_method_val(::Val{2}) = 2 + the_method_val(::Val{3}) = 3 + the_method_val(::Val{4}) = 4 + the_method_val(::Val{5}) = 5 + Base.@constprop :aggressive the_method(x) = the_method_val(Val{x}()) + the_method(2) + end + """) + Base.compilecache(Base.PkgId("DefineTheMethod")) + write(joinpath(load_path, "CallTheMethod.jl"), + """ + module CallTheMethod + using DefineTheMethod + call_the_method() = the_method(1) + call_the_method() + end + """) + Base.compilecache(Base.PkgId("CallTheMethod")) + @eval using DefineTheMethod + @eval using CallTheMethod + @eval DefineTheMethod.the_method_val(::Val{1}) = Int(0) + invokelatest() do + @test Int(0) == CallTheMethod.call_the_method() + end +end + finish_precompile_test!() From 0b9525bf81a1727e18d3342a6da7f8505bc447f5 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Fri, 31 Jan 2025 12:08:26 -0500 Subject: [PATCH 52/56] REPL: generate tab completion hints on a worker thread to not block typing (#57192) --- stdlib/REPL/src/LineEdit.jl | 110 ++++++++++++++++++++++++------------ stdlib/REPL/src/REPL.jl | 10 ++-- 2 files changed, 79 insertions(+), 41 deletions(-) diff --git a/stdlib/REPL/src/LineEdit.jl b/stdlib/REPL/src/LineEdit.jl index e15807f645119..0f9979d768af3 100644 --- a/stdlib/REPL/src/LineEdit.jl +++ b/stdlib/REPL/src/LineEdit.jl @@ -77,9 +77,12 @@ mutable struct MIState last_action::Symbol current_action::Symbol async_channel::Channel{Function} + line_modify_lock::Base.ReentrantLock + hint_generation_lock::Base.ReentrantLock + n_keys_pressed::Int end -MIState(i, mod, c, a, m) = MIState(i, mod, mod, c, a, m, String[], 0, Char[], 0, :none, :none, Channel{Function}()) +MIState(i, mod, c, a, m) = MIState(i, mod, mod, c, a, m, String[], 0, Char[], 0, :none, :none, Channel{Function}(), Base.ReentrantLock(), Base.ReentrantLock(), 0) const BufferLike = Union{MIState,ModeState,IOBuffer} const State = Union{MIState,ModeState} @@ -400,47 +403,82 @@ function complete_line_named(args...; kwargs...)::Tuple{Vector{NamedCompletion}, end end -function check_for_hint(s::MIState) +# checks for a hint and shows it if appropriate. +# to allow the user to type even if hint generation is slow, the +# hint is generated on a worker thread, and only shown if the user hasn't +# pressed a key since the hint generation was requested +function check_show_hint(s::MIState) st = state(s) + + this_key_i = s.n_keys_pressed + next_key_pressed() = @lock s.line_modify_lock s.n_keys_pressed > this_key_i + function lock_clear_hint() + @lock s.line_modify_lock begin + next_key_pressed() || s.aborted || clear_hint(st) && refresh_line(s) + end + end + if !options(st).hint_tab_completes || !eof(buffer(st)) # only generate hints if enabled and at the end of the line # TODO: maybe show hints for insertions at other positions # Requires making space for them earlier in refresh_multi_line - return clear_hint(st) + lock_clear_hint() + return end - - named_completions, partial, should_complete = try - complete_line_named(st.p.complete, st, s.active_module; hint = true) - catch - @debug "error completing line for hint" exception=current_exceptions() - return clear_hint(st) - end - completions = map(x -> x.completion, named_completions) - - isempty(completions) && return clear_hint(st) - # Don't complete for single chars, given e.g. `x` completes to `xor` - if length(partial) > 1 && should_complete - singlecompletion = length(completions) == 1 - p = singlecompletion ? completions[1] : common_prefix(completions) - if singlecompletion || p in completions # i.e. complete `@time` even though `@time_imports` etc. exists - # The completion `p` and the input `partial` may not share the same initial - # characters, for instance when completing to subscripts or superscripts. - # So, in general, make sure that the hint starts at the correct position by - # incrementing its starting position by as many characters as the input. - startind = 1 # index of p from which to start providing the hint - maxind = ncodeunits(p) - for _ in partial - startind = nextind(p, startind) - startind > maxind && break + t_completion = Threads.@spawn :default begin + named_completions, partial, should_complete = nothing, nothing, nothing + + # only allow one task to generate hints at a time and check around lock + # if the user has pressed a key since the hint was requested, to skip old completions + next_key_pressed() && return + @lock s.hint_generation_lock begin + next_key_pressed() && return + named_completions, partial, should_complete = try + complete_line_named(st.p.complete, st, s.active_module; hint = true) + catch + lock_clear_hint() + return end - if startind ≤ maxind # completion on a complete name returns itself so check that there's something to hint - hint = p[startind:end] - st.hint = hint - return true + end + next_key_pressed() && return + + completions = map(x -> x.completion, named_completions) + if isempty(completions) + lock_clear_hint() + return + end + # Don't complete for single chars, given e.g. `x` completes to `xor` + if length(partial) > 1 && should_complete + singlecompletion = length(completions) == 1 + p = singlecompletion ? completions[1] : common_prefix(completions) + if singlecompletion || p in completions # i.e. complete `@time` even though `@time_imports` etc. exists + # The completion `p` and the input `partial` may not share the same initial + # characters, for instance when completing to subscripts or superscripts. + # So, in general, make sure that the hint starts at the correct position by + # incrementing its starting position by as many characters as the input. + startind = 1 # index of p from which to start providing the hint + maxind = ncodeunits(p) + for _ in partial + startind = nextind(p, startind) + startind > maxind && break + end + if startind ≤ maxind # completion on a complete name returns itself so check that there's something to hint + hint = p[startind:end] + next_key_pressed() && return + @lock s.line_modify_lock begin + if !s.aborted + state(s).hint = hint + refresh_line(s) + end + end + return + end end end + lock_clear_hint() end - return clear_hint(st) + Base.errormonitor(t_completion) + return end function clear_hint(s::ModeState) @@ -2569,7 +2607,7 @@ AnyDict( "^_" => (s::MIState,o...)->edit_undo!(s), "\e_" => (s::MIState,o...)->edit_redo!(s), # Show hints at what tab complete would do by default - "*" => (s::MIState,data,c::StringLike)->(edit_insert(s, c); check_for_hint(s) && refresh_line(s)), + "*" => (s::MIState,data,c::StringLike)->(edit_insert(s, c); check_show_hint(s)), "^U" => (s::MIState,o...)->edit_kill_line_backwards(s), "^K" => (s::MIState,o...)->edit_kill_line_forwards(s), "^Y" => (s::MIState,o...)->edit_yank(s), @@ -2875,10 +2913,9 @@ keymap_data(ms::MIState, m::ModalInterface) = keymap_data(state(ms), mode(ms)) function prompt!(term::TextTerminal, prompt::ModalInterface, s::MIState = init_state(term, prompt)) Base.reseteof(term) - l = Base.ReentrantLock() t1 = Threads.@spawn :interactive while true wait(s.async_channel) - status = @lock l begin + status = @lock s.line_modify_lock begin fcn = take!(s.async_channel) fcn(s) end @@ -2893,7 +2930,8 @@ function prompt!(term::TextTerminal, prompt::ModalInterface, s::MIState = init_s # and we want to not block typing when the repl task thread is busy t2 = Threads.@spawn :interactive while true eof(term) || peek(term) # wait before locking but don't consume - @lock l begin + @lock s.line_modify_lock begin + s.n_keys_pressed += 1 kmap = keymap(s, prompt) fcn = match_input(kmap, s) kdata = keymap_data(s, prompt) diff --git a/stdlib/REPL/src/REPL.jl b/stdlib/REPL/src/REPL.jl index 699024c1723a4..cc4f4f00cf8f6 100644 --- a/stdlib/REPL/src/REPL.jl +++ b/stdlib/REPL/src/REPL.jl @@ -1431,7 +1431,7 @@ function setup_interface( end else edit_insert(s, ';') - LineEdit.check_for_hint(s) && LineEdit.refresh_line(s) + LineEdit.check_show_hint(s) end end, '?' => function (s::MIState,o...) @@ -1442,7 +1442,7 @@ function setup_interface( end else edit_insert(s, '?') - LineEdit.check_for_hint(s) && LineEdit.refresh_line(s) + LineEdit.check_show_hint(s) end end, ']' => function (s::MIState,o...) @@ -1465,8 +1465,8 @@ function setup_interface( transition(s, mode) do LineEdit.state(s, mode).input_buffer = buf end - if !isempty(s) && @invokelatest(LineEdit.check_for_hint(s)) - @invokelatest(LineEdit.refresh_line(s)) + if !isempty(s) + @invokelatest(LineEdit.check_show_hint(s)) end break end @@ -1479,7 +1479,7 @@ function setup_interface( Base.errormonitor(t_replswitch) else edit_insert(s, ']') - LineEdit.check_for_hint(s) && LineEdit.refresh_line(s) + LineEdit.check_show_hint(s) end end, From cba19bf8e3ab7f0d509351e0185d60c148facd5b Mon Sep 17 00:00:00 2001 From: Cody Tapscott <84105208+topolarity@users.noreply.github.com> Date: Fri, 31 Jan 2025 17:46:03 -0500 Subject: [PATCH 53/56] contrib: add `--relative-rpath` option to `juliac.jl` (#57199) This flag is useful when you'd like to deploy the generated library / executable across machines. This will tell the linker to look for the required libraries (`libjulia*.so`, `libopenblas`, etc.) in an adjacent `julia/` directory. We'll eventually use PackageCompiler to help bundle all the required libraries together, but for now this at least gets the lookup setup correctly in the created `.so` / binary. --------- Co-authored-by: Gabriel Baraldi --- contrib/julia-config.jl | 57 ++++++++++++++++++++++++----------------- contrib/juliac.jl | 36 ++++++++++++++++++++++---- 2 files changed, 65 insertions(+), 28 deletions(-) diff --git a/contrib/julia-config.jl b/contrib/julia-config.jl index 8b1eb55cbe4f4..6794834834d25 100755 --- a/contrib/julia-config.jl +++ b/contrib/julia-config.jl @@ -45,8 +45,8 @@ function includeDir() return abspath(Sys.BINDIR, Base.INCLUDEDIR, "julia") end -function ldflags(doframework) - doframework && return "-F$(shell_escape(frameworkDir()))" +function ldflags(; framework::Bool=false) + framework && return "-F$(shell_escape(frameworkDir()))" fl = "-L$(shell_escape(libDir()))" if Sys.iswindows() fl = fl * " -Wl,--stack,8388608" @@ -56,27 +56,40 @@ function ldflags(doframework) return fl end -function ldlibs(doframework) +function ldrpath() + libname = if Base.isdebugbuild() + "julia-debug" + else + "julia" + end + return "-Wl,-rpath,$(shell_escape(private_libDir())) -Wl,-rpath,$(shell_escape(libDir())) -l$libname" +end + +function ldlibs(; framework::Bool=false, rpath::Bool=true) # Return "Julia" for the framework even if this is a debug build. # If the user wants the debug framework, DYLD_IMAGE_SUFFIX=_debug # should be used (refer to man 1 dyld). - doframework && return "-framework $(Base.DARWIN_FRAMEWORK_NAME)" + framework && return "-framework $(Base.DARWIN_FRAMEWORK_NAME)" libname = if Base.isdebugbuild() "julia-debug" else "julia" end if Sys.isunix() - return "-L$(shell_escape(private_libDir())) -Wl,-rpath,$(shell_escape(libDir())) -Wl,-rpath,$(shell_escape(private_libDir())) -l$libname" + if rpath + return "-L$(shell_escape(private_libDir())) $(ldrpath())" + else + return "-L$(shell_escape(private_libDir()))" + end else return "-l$libname -lopenlibm" end end -function cflags(doframework) +function cflags(; framework::Bool=false) flags = IOBuffer() print(flags, "-std=gnu11") - if doframework + if framework include = shell_escape(frameworkDir()) print(flags, " -F", include) else @@ -89,8 +102,8 @@ function cflags(doframework) return String(take!(flags)) end -function allflags(doframework) - return "$(cflags(doframework)) $(ldflags(doframework)) $(ldlibs(doframework))" +function allflags(; framework::Bool=false, rpath::Bool=true) + return "$(cflags(; framework)) $(ldflags(; framework)) $(ldlibs(; framework, rpath))" end function check_args(args) @@ -102,31 +115,29 @@ function check_args(args) end function check_framework_flag(args) - doframework = "--framework" in args - if doframework && !Base.DARWIN_FRAMEWORK + framework = "--framework" in args + if framework && !Base.DARWIN_FRAMEWORK println(stderr, "NOTICE: Ignoring --framework because Julia is not packaged as a framework.") return false - elseif !doframework && Base.DARWIN_FRAMEWORK + elseif !framework && Base.DARWIN_FRAMEWORK println(stderr, "NOTICE: Consider using --framework because Julia is packaged as a framework.") return false end - return doframework + return framework end -function main() - check_args(ARGS) - doframework = check_framework_flag(ARGS) - for args in ARGS +function (@main)(args) + check_args(args) + framework = check_framework_flag(args) + for args in args if args == "--ldflags" - println(ldflags(doframework)) + println(ldflags(; framework)) elseif args == "--cflags" - println(cflags(doframework)) + println(cflags(; framework)) elseif args == "--ldlibs" - println(ldlibs(doframework)) + println(ldlibs(; framework)) elseif args == "--allflags" - println(allflags(doframework)) + println(allflags(; framework)) end end end - -main() diff --git a/contrib/juliac.jl b/contrib/juliac.jl index 3d1a3ef13fe79..8b413fccd6231 100644 --- a/contrib/juliac.jl +++ b/contrib/juliac.jl @@ -1,11 +1,16 @@ # Julia compiler wrapper script # NOTE: The interface and location of this script are considered unstable/experimental +module JuliaConfig + include(joinpath(@__DIR__, "julia-config.jl")) +end + julia_cmd = `$(Base.julia_cmd()) --startup-file=no --history-file=no` output_type = nothing # exe, sharedlib, sysimage outname = nothing file = nothing add_ccallables = false +relative_rpath = false verbose = false help = findfirst(x->x == "--help", ARGS) @@ -15,6 +20,7 @@ if help !== nothing Usage: julia juliac.jl [--output-exe | --output-lib | --output-sysimage] [options] --experimental --trim= Only output code statically determined to be reachable --compile-ccallable Include all methods marked `@ccallable` in output + --relative-rpath Configure the library / executable to lookup all required libraries in an adjacent "julia/" folder --verbose Request verbose output """) exit(0) @@ -36,6 +42,8 @@ let i = 1 global add_ccallables = true elseif arg == "--verbose" global verbose = true + elseif arg == "--relative-rpath" + global relative_rpath = true elseif startswith(arg, "--trim") || arg == "--experimental" # forwarded args push!(julia_args, arg) @@ -53,11 +61,27 @@ end isnothing(outname) && error("No output file specified") isnothing(file) && error("No input file specified") +function get_rpath(; relative::Bool = false) + if relative + if Sys.isapple() + return "-Wl,-rpath,'@loader_path/julia/' -Wl,-rpath,'@loader_path/'" + elseif Sys.islinux() + return "-Wl,-rpath,'\$ORIGIN/julia/' -Wl,-rpath,'\$ORIGIN/'" + else + error("unimplemented") + end + else + return JuliaConfig.ldrpath() + end +end + absfile = abspath(file) -cflags = readchomp(`$(julia_cmd) $(joinpath(Sys.BINDIR, Base.DATAROOTDIR,"julia", "julia-config.jl")) --cflags `) +cflags = JuliaConfig.cflags(; framework=false) cflags = Base.shell_split(cflags) -allflags = readchomp(`$(julia_cmd) $(joinpath(Sys.BINDIR, Base.DATAROOTDIR,"julia", "julia-config.jl")) --allflags`) +allflags = JuliaConfig.allflags(; framework=false, rpath=false) allflags = Base.shell_split(allflags) +rpath = get_rpath(; relative = relative_rpath) +rpath = Base.shell_split(rpath) tmpdir = mktempdir(cleanup=false) initsrc_path = joinpath(tmpdir, "init.c") init_path = joinpath(tmpdir, "init.a") @@ -113,12 +137,14 @@ function link_products() julia_libs = Base.shell_split(Base.isdebugbuild() ? "-ljulia-debug -ljulia-internal-debug" : "-ljulia -ljulia-internal") try if output_type == "--output-lib" - run(`cc $(allflags) -o $outname -shared -Wl,$(Base.Linking.WHOLE_ARCHIVE) $img_path -Wl,$(Base.Linking.NO_WHOLE_ARCHIVE) $init_path $(julia_libs)`) + cmd2 = `cc $(allflags) $(rpath) -o $outname -shared -Wl,$(Base.Linking.WHOLE_ARCHIVE) $img_path -Wl,$(Base.Linking.NO_WHOLE_ARCHIVE) $init_path $(julia_libs)` elseif output_type == "--output-sysimage" - run(`cc $(allflags) -o $outname -shared -Wl,$(Base.Linking.WHOLE_ARCHIVE) $img_path -Wl,$(Base.Linking.NO_WHOLE_ARCHIVE) $(julia_libs)`) + cmd2 = `cc $(allflags) $(rpath) -o $outname -shared -Wl,$(Base.Linking.WHOLE_ARCHIVE) $img_path -Wl,$(Base.Linking.NO_WHOLE_ARCHIVE) $(julia_libs)` else - run(`cc $(allflags) -o $outname -Wl,$(Base.Linking.WHOLE_ARCHIVE) $img_path -Wl,$(Base.Linking.NO_WHOLE_ARCHIVE) $init_path $(julia_libs)`) + cmd2 = `cc $(allflags) $(rpath) -o $outname -Wl,$(Base.Linking.WHOLE_ARCHIVE) $img_path -Wl,$(Base.Linking.NO_WHOLE_ARCHIVE) $init_path $(julia_libs)` end + verbose && println("Running: $cmd2") + run(cmd2) catch e println("\nCompilation failed: ", e) exit(1) From 9a278a21650561c79a6b8e6158873cabefdcc2c1 Mon Sep 17 00:00:00 2001 From: DilumAluthgeBot <43731525+DilumAluthgeBot@users.noreply.github.com> Date: Fri, 31 Jan 2025 23:36:22 -0500 Subject: [PATCH 54/56] =?UTF-8?q?=F0=9F=A4=96=20[master]=20Bump=20the=20Li?= =?UTF-8?q?nearAlgebra=20stdlib=20from=20da6d052=20to=2057e9a0d=20(#57177)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Stdlib: LinearAlgebra URL: https://github.com/JuliaLang/LinearAlgebra.jl.git Stdlib branch: master Julia branch: master Old commit: da6d052 New commit: 57e9a0d Julia version: 1.12.0-DEV LinearAlgebra version: 1.12.0 Bump invoked by: @ViralBShah Powered by: [BumpStdlibs.jl](https://github.com/JuliaLang/BumpStdlibs.jl) Diff: https://github.com/JuliaLang/LinearAlgebra.jl/compare/da6d0521347daf5e42b3d09cdb757d4488528c7b...57e9a0d19f7a0042e0ef536fe5ae5fc35c61a6f1 ``` $ git log --oneline da6d052..57e9a0d 57e9a0d Reduce allocations and improve performance in `syevr!` (#1176) 8bb9f6b fix error messages (#1171) 97a712f Update .ci/Manifest.toml (#1179) ``` Co-authored-by: ViralBShah <744411+ViralBShah@users.noreply.github.com> Co-authored-by: Viral B. Shah --- .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 - .../sha512 | 1 - stdlib/LinearAlgebra.version | 2 +- 5 files changed, 3 insertions(+), 3 deletions(-) create mode 100644 deps/checksums/LinearAlgebra-57e9a0d19f7a0042e0ef536fe5ae5fc35c61a6f1.tar.gz/md5 create mode 100644 deps/checksums/LinearAlgebra-57e9a0d19f7a0042e0ef536fe5ae5fc35c61a6f1.tar.gz/sha512 delete mode 100644 deps/checksums/LinearAlgebra-da6d0521347daf5e42b3d09cdb757d4488528c7b.tar.gz/md5 delete mode 100644 deps/checksums/LinearAlgebra-da6d0521347daf5e42b3d09cdb757d4488528c7b.tar.gz/sha512 diff --git a/deps/checksums/LinearAlgebra-57e9a0d19f7a0042e0ef536fe5ae5fc35c61a6f1.tar.gz/md5 b/deps/checksums/LinearAlgebra-57e9a0d19f7a0042e0ef536fe5ae5fc35c61a6f1.tar.gz/md5 new file mode 100644 index 0000000000000..cf0897401a9b8 --- /dev/null +++ b/deps/checksums/LinearAlgebra-57e9a0d19f7a0042e0ef536fe5ae5fc35c61a6f1.tar.gz/md5 @@ -0,0 +1 @@ +a2c4227b17fe589791261081710add05 diff --git a/deps/checksums/LinearAlgebra-57e9a0d19f7a0042e0ef536fe5ae5fc35c61a6f1.tar.gz/sha512 b/deps/checksums/LinearAlgebra-57e9a0d19f7a0042e0ef536fe5ae5fc35c61a6f1.tar.gz/sha512 new file mode 100644 index 0000000000000..3aebc8a8bd7b5 --- /dev/null +++ b/deps/checksums/LinearAlgebra-57e9a0d19f7a0042e0ef536fe5ae5fc35c61a6f1.tar.gz/sha512 @@ -0,0 +1 @@ +08fd85a63a08aae079c835f139845f5155fbb5d96444e8ed4cd1a696921e30ec842e6013e486e32d7ffe19d598ff1abb084e0a13fecdecc17546a76f16357a4a diff --git a/deps/checksums/LinearAlgebra-da6d0521347daf5e42b3d09cdb757d4488528c7b.tar.gz/md5 b/deps/checksums/LinearAlgebra-da6d0521347daf5e42b3d09cdb757d4488528c7b.tar.gz/md5 deleted file mode 100644 index 1771370455266..0000000000000 --- a/deps/checksums/LinearAlgebra-da6d0521347daf5e42b3d09cdb757d4488528c7b.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -810a9f842ee40d03e55c698130620963 diff --git a/deps/checksums/LinearAlgebra-da6d0521347daf5e42b3d09cdb757d4488528c7b.tar.gz/sha512 b/deps/checksums/LinearAlgebra-da6d0521347daf5e42b3d09cdb757d4488528c7b.tar.gz/sha512 deleted file mode 100644 index f696212eda4fc..0000000000000 --- a/deps/checksums/LinearAlgebra-da6d0521347daf5e42b3d09cdb757d4488528c7b.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -4197a863c0f5052f0b85f1c04b6f42f71bf35af572f507dbc475e868a7c057bebd11f8258451b7d0a728012f238abfd88943fe80b83df63728af2fcc01199604 diff --git a/stdlib/LinearAlgebra.version b/stdlib/LinearAlgebra.version index e877475771829..6bdb43b93bfa8 100644 --- a/stdlib/LinearAlgebra.version +++ b/stdlib/LinearAlgebra.version @@ -1,4 +1,4 @@ LINEARALGEBRA_BRANCH = master -LINEARALGEBRA_SHA1 = da6d0521347daf5e42b3d09cdb757d4488528c7b +LINEARALGEBRA_SHA1 = 57e9a0d19f7a0042e0ef536fe5ae5fc35c61a6f1 LINEARALGEBRA_GIT_URL := https://github.com/JuliaLang/LinearAlgebra.jl.git LINEARALGEBRA_TAR_URL = https://api.github.com/repos/JuliaLang/LinearAlgebra.jl/tarball/$1 From 832f0847ca1674e82b3576281e89b8cd2195f7d1 Mon Sep 17 00:00:00 2001 From: Gabriel Baraldi Date: Sat, 1 Feb 2025 16:36:00 -0300 Subject: [PATCH 55/56] Don't delete inferred code from external interpreters (#57224) We were deleting code from external abstract interpreters since https://github.com/JuliaLang/julia/commit/bdf8219ee80557ea6035b421b00d91b1174234f2. Fixes https://github.com/JuliaGPU/CUDA.jl/issues/2637. Not sure how to write tests for this :( --------- Co-authored-by: Jeff Bezanson --- src/staticdata.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/staticdata.c b/src/staticdata.c index b5d6fb7cdd62a..ba9c6ed8c1bea 100644 --- a/src/staticdata.c +++ b/src/staticdata.c @@ -939,6 +939,7 @@ static void jl_insert_into_serialization_queue(jl_serializer_state *s, jl_value_ record_field_change((jl_value_t**)&ci->inferred, jl_nothing); } else if (native_functions && // don't delete any code if making a ji file + (ci->owner == jl_nothing) && // don't delete code for external interpreters !effects_foldable(jl_atomic_load_relaxed(&ci->ipo_purity_bits)) && // don't delete code we may want for irinterp jl_ir_inlining_cost(inferred) == UINT16_MAX) { // don't delete inlineable code // delete the code now: if we thought it was worth keeping, it would have been converted to object code From cee26a35c1b16407ce624f9f0e154a41ca60d8e8 Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Sun, 2 Feb 2025 01:29:55 +0100 Subject: [PATCH 56/56] allow passing custom julia arguments in `runtests` (#56716) The `propagate_project` kwarg can be removed after current users (LinearAlgebra.jl) switches to it. This is to allow moving buildkite yaml logic into Julia where it is easier to write. --------- Co-authored-by: Alex Arslan --- base/util.jl | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/base/util.jl b/base/util.jl index c01ff697e64e3..39ca793a7c3f5 100644 --- a/base/util.jl +++ b/base/util.jl @@ -678,7 +678,7 @@ end """ Base.runtests(tests=["all"]; ncores=ceil(Int, Sys.CPU_THREADS / 2), - exit_on_error=false, revise=false, propagate_project=true, [seed]) + exit_on_error=false, revise=false, propagate_project=true, [seed], [julia_args::Cmd]) Run the Julia unit tests listed in `tests`, which can be either a string or an array of strings, using `ncores` processors. If `exit_on_error` is `false`, when one test @@ -689,12 +689,14 @@ to the standard libraries before running the tests. If `propagate_project` is true the current project is propagated to the test environment. If a seed is provided via the keyword argument, it is used to seed the global RNG in the context where the tests are run; otherwise the seed is chosen randomly. +The argument `julia_args` can be used to pass custom `julia` command line flags to the test process. """ function runtests(tests = ["all"]; ncores::Int = ceil(Int, Sys.CPU_THREADS / 2), exit_on_error::Bool=false, revise::Bool=false, propagate_project::Bool=false, - seed::Union{BitInteger,Nothing}=nothing) + seed::Union{BitInteger,Nothing}=nothing, + julia_args::Cmd=``) if isa(tests,AbstractString) tests = split(tests) end @@ -710,7 +712,7 @@ function runtests(tests = ["all"]; ncores::Int = ceil(Int, Sys.CPU_THREADS / 2), delete!(ENV2, "JULIA_PROJECT") project_flag = propagate_project ? `--project` : `` try - run(setenv(`$(julia_cmd()) $project_flag $(joinpath(Sys.BINDIR, + run(setenv(`$(julia_cmd()) $julia_args $project_flag $(joinpath(Sys.BINDIR, Base.DATAROOTDIR, "julia", "test", "runtests.jl")) $tests`, ENV2)) nothing catch