From de85ebb2dab5519a2bd337cb62ff7d17dcc8f6c3 Mon Sep 17 00:00:00 2001 From: DilumAluthgeBot <43731525+DilumAluthgeBot@users.noreply.github.com> Date: Mon, 29 Dec 2025 15:41:55 -0500 Subject: [PATCH 01/39] =?UTF-8?q?=F0=9F=A4=96=20[backports-release-1.12]?= =?UTF-8?q?=20Bump=20NetworkOptions=20stdlib=20532992f=20=E2=86=92=207034c?= =?UTF-8?q?55=20(#60505)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Stdlib: NetworkOptions URL: https://github.com/JuliaLang/NetworkOptions.jl.git Stdlib branch: master Julia branch: backports-release-1.12 Old commit: 532992f New commit: 7034c55 Julia version: 1.12.3 NetworkOptions version: 1.3.0 (Does not match) Bump invoked by: @fingolfin Powered by: [BumpStdlibs.jl](https://github.com/JuliaLang/BumpStdlibs.jl) Diff: https://github.com/JuliaLang/NetworkOptions.jl/compare/532992fcc0f1d02df48374969cbae37e34c01360...7034c55dbf52ee959cabd63bcbe656df658f5bda ``` $ git log --oneline 532992f..7034c55 7034c55 Bump actions/checkout from 4 to 5 (#44) 46e14ef Merge pull request #43 from lgoettgens/lg/JET-ca_roots-path_convert ``` Co-authored-by: fingolfin <241512+fingolfin@users.noreply.github.com> --- .../md5 | 1 - .../sha512 | 1 - .../md5 | 1 + .../sha512 | 1 + stdlib/NetworkOptions.version | 2 +- 5 files changed, 3 insertions(+), 3 deletions(-) delete mode 100644 deps/checksums/NetworkOptions-532992fcc0f1d02df48374969cbae37e34c01360.tar.gz/md5 delete mode 100644 deps/checksums/NetworkOptions-532992fcc0f1d02df48374969cbae37e34c01360.tar.gz/sha512 create mode 100644 deps/checksums/NetworkOptions-7034c55dbf52ee959cabd63bcbe656df658f5bda.tar.gz/md5 create mode 100644 deps/checksums/NetworkOptions-7034c55dbf52ee959cabd63bcbe656df658f5bda.tar.gz/sha512 diff --git a/deps/checksums/NetworkOptions-532992fcc0f1d02df48374969cbae37e34c01360.tar.gz/md5 b/deps/checksums/NetworkOptions-532992fcc0f1d02df48374969cbae37e34c01360.tar.gz/md5 deleted file mode 100644 index fbb7d5b86627f..0000000000000 --- a/deps/checksums/NetworkOptions-532992fcc0f1d02df48374969cbae37e34c01360.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -afab093d162a62d5a488894f33d3b396 diff --git a/deps/checksums/NetworkOptions-532992fcc0f1d02df48374969cbae37e34c01360.tar.gz/sha512 b/deps/checksums/NetworkOptions-532992fcc0f1d02df48374969cbae37e34c01360.tar.gz/sha512 deleted file mode 100644 index 146d3a3d1bad8..0000000000000 --- a/deps/checksums/NetworkOptions-532992fcc0f1d02df48374969cbae37e34c01360.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -14b41cc9c93e2f9eeaa9499d65b8c42ee80691cbd533ef6cafabdb6e94c7cf31eee00fb603ca70dfe86930c871419cf17a8f05c0a76bd379a8bbf705b875dfe2 diff --git a/deps/checksums/NetworkOptions-7034c55dbf52ee959cabd63bcbe656df658f5bda.tar.gz/md5 b/deps/checksums/NetworkOptions-7034c55dbf52ee959cabd63bcbe656df658f5bda.tar.gz/md5 new file mode 100644 index 0000000000000..8f918b40b2ffb --- /dev/null +++ b/deps/checksums/NetworkOptions-7034c55dbf52ee959cabd63bcbe656df658f5bda.tar.gz/md5 @@ -0,0 +1 @@ +1fbf59e3052ec0d40a195d935b3d4a96 diff --git a/deps/checksums/NetworkOptions-7034c55dbf52ee959cabd63bcbe656df658f5bda.tar.gz/sha512 b/deps/checksums/NetworkOptions-7034c55dbf52ee959cabd63bcbe656df658f5bda.tar.gz/sha512 new file mode 100644 index 0000000000000..feeb688dcb7e2 --- /dev/null +++ b/deps/checksums/NetworkOptions-7034c55dbf52ee959cabd63bcbe656df658f5bda.tar.gz/sha512 @@ -0,0 +1 @@ +4ec9724062d97a9d400bfb4a672ed5ce52999738ddbc01d2892d97df3fd256fe03bb5ea69f2ebbbbdbfef91edc24d82e54df48f997781eba2c6cd8e1c36d046c diff --git a/stdlib/NetworkOptions.version b/stdlib/NetworkOptions.version index f7cb50a74d106..7a3c0ccd27d3f 100644 --- a/stdlib/NetworkOptions.version +++ b/stdlib/NetworkOptions.version @@ -1,4 +1,4 @@ NETWORKOPTIONS_BRANCH = master -NETWORKOPTIONS_SHA1 = 532992fcc0f1d02df48374969cbae37e34c01360 +NETWORKOPTIONS_SHA1 = 7034c55dbf52ee959cabd63bcbe656df658f5bda NETWORKOPTIONS_GIT_URL := https://github.com/JuliaLang/NetworkOptions.jl.git NETWORKOPTIONS_TAR_URL = https://api.github.com/repos/JuliaLang/NetworkOptions.jl/tarball/$1 From a198393a305dc169e9d997cc457a0cec190c0cc1 Mon Sep 17 00:00:00 2001 From: David Anthoff Date: Tue, 23 Dec 2025 13:44:35 -0800 Subject: [PATCH 02/39] Docs: mention cmd line option for App Installer on Windows (cherry picked from commit 7450aba3420729c112541894056d474b934c1a1c) --- doc/src/manual/installation.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/doc/src/manual/installation.md b/doc/src/manual/installation.md index f45aba2c37a28..5107d30c7286f 100644 --- a/doc/src/manual/installation.md +++ b/doc/src/manual/installation.md @@ -75,7 +75,12 @@ If the Windows Store is blocked on a system, we have an alternative [MSIX App Installer](https://learn.microsoft.com/en-us/windows/msix/app-installer/app-installer-file-overview) based setup. To use the App Installer version, download [this](https://install.julialang.org/Julia.appinstaller) file and open it by -double clicking on it. +double clicking on it. One can also install exactly the same version by executing +the PowerShell command + +``` +Add-AppxPackage -AppInstallerFile https://install.julialang.org/Julia.appinstaller +``` ### MSI Installer (Windows) From 5ae7e55f07a679aae390395be82c6c2186cfb0ca Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Thu, 25 Dec 2025 06:55:14 -0500 Subject: [PATCH 03/39] REPL: fix completion overwriting input after cursor (#60472) (cherry picked from commit b6337dd711a0166bf7574f49b4446d5577cb7d3d) --- stdlib/REPL/src/REPLCompletions.jl | 11 +++++----- stdlib/REPL/test/replcompletions.jl | 32 +++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 5 deletions(-) diff --git a/stdlib/REPL/src/REPLCompletions.jl b/stdlib/REPL/src/REPLCompletions.jl index cd13f0880d7c4..0a5975179e3a3 100644 --- a/stdlib/REPL/src/REPLCompletions.jl +++ b/stdlib/REPL/src/REPLCompletions.jl @@ -1025,8 +1025,8 @@ function completions(string::String, pos::Int, context_module::Module=Main, shif if obj !== nothing # Skip leading whitespace inside brackets. i = @something findnext(!isspace, string, first(key)) nextind(string, last(key)) - key = i:last(key) - s = string[intersect(key, 1:pos)] + key = intersect(i:last(key), 1:pos) + s = string[key] matches = find_dict_matches(obj, s) length(matches) == 1 && !closed && (matches[1] *= ']') if length(matches) > 0 @@ -1051,7 +1051,8 @@ function completions(string::String, pos::Int, context_module::Module=Main, shif # "~/example.txt TAB => "/home/user/example.txt" r, closed = find_str(cur) if r !== nothing - s = do_string_unescape(string[intersect(r, 1:pos)]) + r = intersect(r, 1:pos) + s = do_string_unescape(string[r]) ret, success = complete_path_string(s, hint; string_escape=true, dirsep=Sys.iswindows() ? '\\' : '/') if length(ret) == 1 && !closed && close_path_completion(ret[1].path) @@ -1094,8 +1095,8 @@ function completions(string::String, pos::Int, context_module::Module=Main, shif # Keyword argument completion: # foo(ar TAB => keyword arguments like `arg1=` elseif kind(cur) == K"Identifier" - r = char_range(cur) - s = string[intersect(r, 1:pos)] + r = intersect(char_range(cur), 1:pos) + s = string[r] # Return without adding more suggestions if kwargs only complete_keyword_argument!(suggestions, e, s, context_module, arg_pos; shift) && return sort_suggestions(), r, true diff --git a/stdlib/REPL/test/replcompletions.jl b/stdlib/REPL/test/replcompletions.jl index de723cf83d0e7..428a13dd59e79 100644 --- a/stdlib/REPL/test/replcompletions.jl +++ b/stdlib/REPL/test/replcompletions.jl @@ -1492,6 +1492,23 @@ mktempdir() do path @test "$(path_expected)$(sep)foo_dir$(sep)" in c @test "$(path_expected)$(sep)foo_file.txt" in c end + + # Issue #60444: path completion should not delete text after the string (e.g. indexing) + let (c, r, res) = test_complete_pos("f(\"$(path)$(sep)foo|\")") + @test res + @test length(c) == 2 + @test "$(path_expected)$(sep)foo_dir$(sep)" in c + @test "$(path_expected)$(sep)foo_file.txt" in c + end + let (c, r, res) = test_complete_pos("f(\"$(path)$(sep)foo|\")[1]") + @test res + @test length(c) == 2 + @test "$(path_expected)$(sep)foo_dir$(sep)" in c + @test "$(path_expected)$(sep)foo_file.txt" in c + # Range should end at cursor position, not overwrite ")[1]" + pos = findfirst('|', "f(\"$(path)$(sep)foo|\")[1]") - 1 + @test last(r) == pos + end end if Sys.iswindows() @@ -1643,6 +1660,14 @@ test_dict_completion("test_repl_comp_customdict") let s = "test_dict_no_length[" @test REPLCompletions.completions(s, sizeof(s), Main.CompletionFoo) isa Tuple end + + # Issue #60444: completing dict keys should not overwrite input after cursor + let s = "test_dict[\"ab|c\"]" + c, r = test_complete_context_pos(s, Main.CompletionFoo) + @test "\"abc\"" in c + @test "\"abcd\"" in c + @test r == 11:13 # range ends at cursor, not at end of key + end end @testset "completion of string/cmd macros (#22577)" begin @@ -1789,6 +1814,13 @@ end @test hasnokwsuggestions("CompletionFoo.kwtest5('a', 3, 5, unknownsplat...; xy") @test hasnokwsuggestions("CompletionFoo.kwtest5(3; somek") =# + + # Issue #60444: completing keyword arguments should not overwrite input after cursor + let s = "CompletionFoo.kwtest3(a; foob|true)" + c, r = test_complete_pos(s) + @test c == ["foobar="] + @test r == 26:29 + end end # Test completion in context From eb2afda8eee200282d138782c187111962e72814 Mon Sep 17 00:00:00 2001 From: Nicholas Bauer Date: Sat, 27 Dec 2025 08:54:58 -0500 Subject: [PATCH 04/39] Clarify that `cpu_info` times are expressed in milliseconds (#60480) libuv reports times in milliseconds, but the Julia docs and show() use units of 1/Sys.SC_CLK_TCK which is 100 on my laptop. The mismatch might be due to libuv changeset https://github.com/libuv/libuv/commit/37a8affbd319209fd0d0d96ee4e3002c8ecc5076. Fixes #53577 (cherry picked from commit 3d611fddcf02cce3fc8dc518e49f25128b8aaead) --- base/sysinfo.jl | 17 +++++------------ test/sysinfo.jl | 6 ++---- 2 files changed, 7 insertions(+), 16 deletions(-) diff --git a/base/sysinfo.jl b/base/sysinfo.jl index 56a3681ae35f7..f6c4ed58f5daa 100644 --- a/base/sysinfo.jl +++ b/base/sysinfo.jl @@ -107,8 +107,6 @@ const WORD_SIZE = Core.sizeof(Int) * 8 The number of system "clock ticks" per second, corresponding to `sysconf(_SC_CLK_TCK)` on POSIX systems, or `0` if it is unknown. - -CPU times, e.g. as returned by `Sys.cpu_info()`, are in units of ticks, i.e. units of `1 / Sys.SC_CLK_TCK` seconds if `Sys.SC_CLK_TCK > 0`. """ global SC_CLK_TCK::Clong @@ -202,8 +200,7 @@ The `CPUinfo` type is a mutable struct with the following fields: - `cpu_times!idle::UInt64`: Time spent in idle mode. CPU state shows the CPU time that's not actively being used. - `cpu_times!irq::UInt64`: Time spent handling interrupts. CPU state shows the amount of time the CPU has been servicing hardware interrupts. -The times are in units of `1/Sys.SC_CLK_TCK` seconds if `Sys.SC_CLK_TCK > 0`; otherwise they are in -unknown units. +The times are in units of milliseconds. Note: Included in the detailed system information via `versioninfo(verbose=true)`. """ @@ -224,7 +221,6 @@ CPUinfo(info::UV_cpu_info_t) = CPUinfo(unsafe_string(info.model), info.speed, public CPUinfo function _show_cpuinfo(io::IO, info::Sys.CPUinfo, header::Bool=true, prefix::AbstractString=" ") - tck = SC_CLK_TCK if header println(io, info.model, ": ") print(io, " "^length(prefix)) @@ -232,16 +228,13 @@ function _show_cpuinfo(io::IO, info::Sys.CPUinfo, header::Bool=true, prefix::Abs lpad("sys", 9), " ", lpad("idle", 9), " ", lpad("irq", 9)) end print(io, prefix) - unit = tck > 0 ? " s " : " " - tc = max(tck, 1) + ms_per_s = 1000 + unit = " s " d(i, unit=unit) = lpad(string(round(Int64,i)), 9) * unit print(io, lpad(string(info.speed), 5), " MHz ", - d(info.cpu_times!user / tc), d(info.cpu_times!nice / tc), d(info.cpu_times!sys / tc), - d(info.cpu_times!idle / tc), d(info.cpu_times!irq / tc, tck > 0 ? " s" : " ")) - if tck <= 0 - print(io, "ticks") - end + d(info.cpu_times!user / ms_per_s), d(info.cpu_times!nice / ms_per_s), d(info.cpu_times!sys / ms_per_s), + d(info.cpu_times!idle / ms_per_s), d(info.cpu_times!irq / ms_per_s)) end show(io::IO, ::MIME"text/plain", info::CPUinfo) = _show_cpuinfo(io, info, true, " ") diff --git a/test/sysinfo.jl b/test/sysinfo.jl index f02d8ffe36091..a2af4cd0f5260 100644 --- a/test/sysinfo.jl +++ b/test/sysinfo.jl @@ -65,9 +65,7 @@ end Base.Sys.CPUinfo("Apple M1 Pro", 2400, 0x00000000026784da, 0x0000000000000000, 0x0000000000fda30e, 0x0000000046a731ea, 0x0000000000000000) Base.Sys.CPUinfo("Apple M1 Pro", 2400, 0x00000000017726c0, 0x0000000000000000, 0x00000000009491de, 0x0000000048134f1e, 0x0000000000000000)] - Sys.SC_CLK_TCK, save_SC_CLK_TCK = 100, Sys.SC_CLK_TCK # use platform-independent tick units @test repr(example_cpus[1]) == "Base.Sys.CPUinfo(\"Apple M1 Pro\", 2400, 0x000000000d913b08, 0x0000000000000000, 0x0000000005f4243c, 0x00000000352a550a, 0x0000000000000000)" - @test repr("text/plain", example_cpus[1]) == "Apple M1 Pro: \n speed user nice sys idle irq\n 2400 MHz 2276216 s 0 s 998861 s 8919667 s 0 s" - @test sprint(Sys.cpu_summary, example_cpus) == "Apple M1 Pro: \n speed user nice sys idle irq\n#1 2400 MHz 2276216 s 0 s 998861 s 8919667 s 0 s\n#2 2400 MHz 2275576 s 0 s 978101 s 8962204 s 0 s\n#3 2400 MHz 403386 s 0 s 166224 s 11853624 s 0 s\n#4 2400 MHz 245859 s 0 s 97367 s 12092250 s 0 s\n" - Sys.SC_CLK_TCK = save_SC_CLK_TCK + @test repr("text/plain", example_cpus[1]) == "Apple M1 Pro: \n speed user nice sys idle irq\n 2400 MHz 227622 s 0 s 99886 s 891967 s 0 s " + @test sprint(Sys.cpu_summary, example_cpus) == "Apple M1 Pro: \n speed user nice sys idle irq\n#1 2400 MHz 227622 s 0 s 99886 s 891967 s 0 s \n#2 2400 MHz 227558 s 0 s 97810 s 896220 s 0 s \n#3 2400 MHz 40339 s 0 s 16622 s 1185362 s 0 s \n#4 2400 MHz 24586 s 0 s 9737 s 1209225 s 0 s \n" end From 387dd934500943d5f2361120ad549e260a9554e5 Mon Sep 17 00:00:00 2001 From: N5N3 <2642243996@qq.com> Date: Mon, 29 Dec 2025 19:01:03 +0800 Subject: [PATCH 05/39] Fix `@allocated 1 .+ 1` (#60494) Dot operators (e.g., .+, .-) are lowered to `BroadcastFunction` and do not have corresponding function definitions. Fix the regression by adding the needed wrapper. Test added. --------- Co-authored-by: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> (cherry picked from commit 7ba24a71da45e0f2aa7f7307753620c59269700a) --- base/timing.jl | 8 ++++++++ test/misc.jl | 3 +++ 2 files changed, 11 insertions(+) diff --git a/base/timing.jl b/base/timing.jl index e937d396a52a2..230849c7eb3d7 100644 --- a/base/timing.jl +++ b/base/timing.jl @@ -498,6 +498,14 @@ function is_simply_call(@nospecialize ex) Meta.isexpr(a, :..., 1) && is_simple_atom(a.args[1]) && continue return false end + # Ensure Expr(:call, .+, ...) get wrapped + if ex.args[1] isa Symbol + sa = String(ex.args[1]::Symbol) + startswith(sa, ".") && + !endswith(sa, ".") && + isoperator(Symbol(sa[2:end])) && + return false + end return true end diff --git a/test/misc.jl b/test/misc.jl index 8d6ee39715303..f830913d21098 100644 --- a/test/misc.jl +++ b/test/misc.jl @@ -1542,6 +1542,9 @@ end @allocated _x = 1+2 @test _x === 3 + # test `@allocated` works for dotted operations + @test (@allocated 1 .+ 1) == 0 + n, m = 10, 20 X = rand(n, m) treshape59278(X, n, m) From 1ed5bdf7655a3b8e8f9dc935d564c164ab5c1e14 Mon Sep 17 00:00:00 2001 From: Max Horn Date: Mon, 29 Dec 2025 18:36:46 +0100 Subject: [PATCH 06/39] Fix JET warning by avoiding recursion in `getproperty(::DirEntry)` (#60506) This lead to JET warnings in e.g. `realpath`. I would say even better would be to avoid this `getproperty` method altogether. I have an alternative patch which adds a `path(::DirEntry)` method and changes code in REPL and tests to avoid it. (cherry picked from commit a4d21299053d0e140561a7969694b93751c9d312) --- base/file.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/file.jl b/base/file.jl index 7c2d8ebe86de8..cece850ea265b 100644 --- a/base/file.jl +++ b/base/file.jl @@ -1059,7 +1059,7 @@ struct DirEntry end function Base.getproperty(obj::DirEntry, p::Symbol) if p === :path - return joinpath(obj.dir, obj.name) + return joinpath(getfield(obj, :dir), getfield(obj, :name)) else return getfield(obj, p) end From 89dc101c04c8366ab567d786430dc9b5db0aacc5 Mon Sep 17 00:00:00 2001 From: Max Horn Date: Mon, 29 Dec 2025 19:11:53 +0100 Subject: [PATCH 07/39] Fix JET warning in `artifact_meta` (#60507) `unpack_platform` might return nothing in case of an error. Together with PR #60506 and my previous changes, when applied to release-1.12, the JET report for GAP.jl is now free of reports. Well, if I filter out certain "unavoidable issues": ```julia using Revise, JET, AbstractAlgebra, GAP struct AnyFrameMethod <: ReportMatcher m::Union{Function,Method,Symbol} end function JET.match_report(matcher::AnyFrameMethod, @nospecialize(report::JET.InferenceErrorReport)) # check all VirtualFrames in the VirtualStackTrace for a match to the specified method m = matcher.m if m isa Symbol return any(vf -> vf.linfo.def.name === m, report.vst) elseif m isa Method return any(vf -> vf.linfo.def === m, report.vst) else # if m isa Function return any(vf -> vf.linfo.def in methods(m), report.vst) end end report_package(GAP; ignored_modules=[AnyFrameMethod(:is_loaded_directly)]) ``` (cherry picked from commit 8123a5cb8256ce2e96d34a14e8200e580e3b8f50) --- stdlib/Artifacts/src/Artifacts.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/Artifacts/src/Artifacts.jl b/stdlib/Artifacts/src/Artifacts.jl index 0227d9532a49c..79cb05e86e354 100644 --- a/stdlib/Artifacts/src/Artifacts.jl +++ b/stdlib/Artifacts/src/Artifacts.jl @@ -410,7 +410,7 @@ function artifact_meta(name::String, artifact_dict::Dict, artifacts_toml::String dl_dict = Dict{Platform,Dict{String,Any}}() for x in meta x = x::Dict{String, Any} - dl_dict[unpack_platform(x, name, artifacts_toml)] = x + dl_dict[unpack_platform(x, name, artifacts_toml)::Platform] = x end meta = select_platform(dl_dict, platform) # If it's NOT a dict, complain From e7b2b231a5349582406950162806c341bcdbc6ba Mon Sep 17 00:00:00 2001 From: Nathan Zimmerberg <39104088+nhz2@users.noreply.github.com> Date: Mon, 12 Jan 2026 11:08:14 -0500 Subject: [PATCH 08/39] [backports-release-1.12] Fix build with USE_SYSTEM_P7ZIP=1 for 1.12 (#60644) fix #60596 for Julia 1.12 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index b2d54d0a7a0e5..fd84ad0e7b033 100644 --- a/Makefile +++ b/Makefile @@ -26,7 +26,7 @@ default: $(JULIA_BUILD_MODE) # contains either "debug" or "release" all: debug release # sort is used to remove potential duplicates -DIRS := $(sort $(build_bindir) $(build_depsbindir) $(build_libdir) $(build_private_libdir) $(build_libexecdir) $(build_includedir) $(build_includedir)/julia $(build_sysconfdir)/julia $(build_datarootdir)/julia $(build_datarootdir)/julia/stdlib $(build_man1dir)) +DIRS := $(sort $(build_bindir) $(build_depsbindir) $(build_libdir) $(build_private_libdir) $(build_private_libexecdir) $(build_libexecdir) $(build_includedir) $(build_includedir)/julia $(build_sysconfdir)/julia $(build_datarootdir)/julia $(build_datarootdir)/julia/stdlib $(build_man1dir)) ifneq ($(BUILDROOT),$(JULIAHOME)) BUILDDIRS := $(BUILDROOT) $(addprefix $(BUILDROOT)/,base src src/flisp src/support src/clangsa cli doc deps stdlib test test/clangsa test/embedding test/gcext test/llvmpasses) BUILDDIRMAKE := $(addsuffix /Makefile,$(BUILDDIRS)) $(BUILDROOT)/sysimage.mk $(BUILDROOT)/pkgimage.mk From a6c2a276abd4412f563e2e0ac39a9e5f1834bc4d Mon Sep 17 00:00:00 2001 From: William Moses Date: Wed, 14 Jan 2026 16:58:23 -0600 Subject: [PATCH 09/39] 1.12: add getter for global variable pointer matching jlvalue (#60685) --- src/aotcompile.cpp | 16 ++++++++++++++++ src/codegen-stubs.c | 1 + src/jl_exported_funcs.inc | 1 + src/julia_internal.h | 1 + 4 files changed, 19 insertions(+) diff --git a/src/aotcompile.cpp b/src/aotcompile.cpp index 6009bd435534c..df8a825df3ffe 100644 --- a/src/aotcompile.cpp +++ b/src/aotcompile.cpp @@ -124,6 +124,22 @@ extern "C" JL_DLLEXPORT_CODEGEN void jl_get_llvm_gvs_impl(void *native_code, memcpy(data, value_map.data(), *num_elements * sizeof(void *)); } +extern "C" JL_DLLEXPORT_CODEGEN void jl_get_llvm_gvs_globals_impl(void *native_code, + size_t *num_elements, void **data) +{ + // map a memory location (jl_value_t or jl_binding_t) to a GlobalVariable + jl_native_code_desc_t *desc = (jl_native_code_desc_t *)native_code; + auto &value_map = desc->jl_sysimg_gvars; + + if (data == NULL) { + *num_elements = value_map.size(); + return; + } + + assert(*num_elements == value_map.size()); + memcpy(data, value_map.data(), *num_elements * sizeof(void *)); +} + extern "C" JL_DLLEXPORT_CODEGEN void jl_get_llvm_external_fns_impl(void *native_code, size_t *num_elements, jl_code_instance_t *data) diff --git a/src/codegen-stubs.c b/src/codegen-stubs.c index 04f38fb9091be..e35fdfddfbab9 100644 --- a/src/codegen-stubs.c +++ b/src/codegen-stubs.c @@ -14,6 +14,7 @@ JL_DLLEXPORT void jl_dump_native_fallback(void *native_code, const char *bc_fname, const char *unopt_bc_fname, const char *obj_fname, const char *asm_fname, ios_t *z, ios_t *s) UNAVAILABLE JL_DLLEXPORT void jl_get_llvm_gvs_fallback(void *native_code, arraylist_t *gvs) UNAVAILABLE +JL_DLLEXPORT void jl_get_llvm_gvs_globals_fallback(void *native_code, arraylist_t *gvs) UNAVAILABLE JL_DLLEXPORT void jl_get_llvm_external_fns_fallback(void *native_code, arraylist_t *gvs) UNAVAILABLE JL_DLLEXPORT void jl_get_llvm_mis_fallback(void *native_code, arraylist_t* MIs) UNAVAILABLE diff --git a/src/jl_exported_funcs.inc b/src/jl_exported_funcs.inc index 61420c7306de9..8d2c1d391cb3c 100644 --- a/src/jl_exported_funcs.inc +++ b/src/jl_exported_funcs.inc @@ -523,6 +523,7 @@ YY(jl_get_LLVM_VERSION) \ YY(jl_dump_native) \ YY(jl_get_llvm_gvs) \ + YY(jl_get_llvm_gvs_globals) \ YY(jl_get_llvm_external_fns) \ YY(jl_get_llvm_mis) \ YY(jl_dump_function_asm) \ diff --git a/src/julia_internal.h b/src/julia_internal.h index c9e1b0e204df6..a74fcfb7903d3 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -2057,6 +2057,7 @@ JL_DLLIMPORT void jl_dump_native(void *native_code, const char *bc_fname, const char *unopt_bc_fname, const char *obj_fname, const char *asm_fname, ios_t *z, ios_t *s, jl_emission_params_t *params); JL_DLLIMPORT void jl_get_llvm_gvs(void *native_code, size_t *num_els, void **gvs); +JL_DLLIMPORT void jl_get_llvm_gvs_globals(void *native_code, size_t *num_els, void **gvs); JL_DLLIMPORT void jl_get_llvm_external_fns(void *native_code, size_t *num_els, jl_code_instance_t *gvs); JL_DLLIMPORT void jl_get_function_id(void *native_code, jl_code_instance_t *ncode, From ab6dd0a0c5b286ed33a934fd74f6d817973122a3 Mon Sep 17 00:00:00 2001 From: Oscar Smith Date: Wed, 14 Jan 2026 18:19:31 -0500 Subject: [PATCH 10/39] Backport `@fastmath x^2` inlining regression to 1.12 (#60686) This isn't a direct backport of https://github.com/JuliaLang/julia/pull/60640/changes because because `llvm.powi.f16.i32` is very broken on LLVM 18 (at least for targets without hardware fp16 support like x86). This is only a performance backport, but it is a pretty important regression that was introduced in 1.12. --- base/fastmath.jl | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/base/fastmath.jl b/base/fastmath.jl index ed686fb92bf34..afae504a8830f 100644 --- a/base/fastmath.jl +++ b/base/fastmath.jl @@ -297,12 +297,13 @@ exp10_fast(x::Union{Float32,Float64}) = Base.Math.exp10_fast(x) # builtins -@inline function pow_fast(x::Float64, y::Integer) +@inline function pow_fast(x::Union{Float32, Float64}, y::Integer) z = y % Int32 z == y ? pow_fast(x, z) : x^y end -pow_fast(x::Float32, y::Integer) = x^y -pow_fast(x::Float64, y::Int32) = ccall("llvm.powi.f64.i32", llvmcall, Float64, (Float64, Int32), x, y) +@inline pow_fast(x::Float16, y::Integer) = Float16(pow_fast(Float32(x), y)) +pow_fast(x::Float64, y::Int32) = ccall("llvm.powi", llvmcall, Float64, (Float64, Int32), x, y) +pow_fast(x::Float32, y::Int32) = ccall("llvm.powi", llvmcall, Float32, (Float32, Int32), x, y) pow_fast(x::FloatTypes, ::Val{p}) where {p} = pow_fast(x, p) # inlines already via llvm.powi @inline pow_fast(x, v::Val) = Base.literal_pow(^, x, v) From 634170eb527cf211799bc16c11e6d0fc38c4befa Mon Sep 17 00:00:00 2001 From: Nathan Zimmerberg <39104088+nhz2@users.noreply.github.com> Date: Sat, 10 Jan 2026 19:08:05 -0500 Subject: [PATCH 11/39] Fix build with USE_SYSTEM_P7ZIP=1 (#60623) (cherry picked from commit 657273aebc81d289eaf888f5bcccd6a4e0fce44b) --- base/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/base/Makefile b/base/Makefile index eb36d6d0a4319..0f7767e0aef37 100644 --- a/base/Makefile +++ b/base/Makefile @@ -183,14 +183,14 @@ endif endef # libexec executables -symlink_p7zip: $(build_bindir)/7z$(EXE) +symlink_p7zip: $(build_private_libexecdir)/7z$(EXE) ifneq ($(USE_SYSTEM_P7ZIP),0) SYMLINK_SYSTEM_LIBRARIES += symlink_p7zip 7Z_PATH := $(shell which 7z$(EXE)) endif -$(build_bindir)/7z$(EXE): +$(build_private_libexecdir)/7z$(EXE): [ -e "$(7Z_PATH)" ] && \ rm -f "$@" && \ ln -sf "$(7Z_PATH)" "$@" From e1e955abe00c0ecba3c993c38f21c7e815bab4b2 Mon Sep 17 00:00:00 2001 From: Gabriel Baraldi Date: Wed, 14 Jan 2026 17:42:43 -0300 Subject: [PATCH 12/39] Don't set the owner of the string needlessly. (#60601) This might cause double frees/use after frees. Found on the mimalloc PR (cherry picked from commit 06def894dc0281a9582ec9138960edf60fb262e8) --- src/gc-stock.c | 2 +- src/genericmemory.c | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/gc-stock.c b/src/gc-stock.c index 55f31b26679ff..52759a2febc61 100644 --- a/src/gc-stock.c +++ b/src/gc-stock.c @@ -629,7 +629,7 @@ void jl_gc_reset_alloc_count(void) JL_NOTSAFEPOINT static void jl_gc_free_memory(jl_genericmemory_t *m, int isaligned) JL_NOTSAFEPOINT { assert(jl_is_genericmemory(m)); - assert(jl_genericmemory_how(m) == 1 || jl_genericmemory_how(m) == 2); + assert(jl_genericmemory_how(m) == 1); char *d = (char*)m->ptr; size_t freed_bytes = memory_block_usable_size(d, isaligned); assert(freed_bytes != 0); diff --git a/src/genericmemory.c b/src/genericmemory.c index 32adc1add3d06..fbdccca2174fa 100644 --- a/src/genericmemory.c +++ b/src/genericmemory.c @@ -200,7 +200,6 @@ JL_DLLEXPORT jl_value_t *jl_genericmemory_to_string(jl_genericmemory_t *m, size_ m->length = 0; if (how != 0) { jl_value_t *o = jl_genericmemory_data_owner_field(m); - jl_genericmemory_data_owner_field(m) = NULL; if (how == 3 && // implies jl_is_string(o) ((mlength + sizeof(void*) + 1 <= GC_MAX_SZCLASS) == (len + sizeof(void*) + 1 <= GC_MAX_SZCLASS))) { if (jl_string_data(o)[len] != '\0') From b2bc423a2dd45da0e4c10de2d5f9cc12e66fdbad Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Thu, 15 Jan 2026 00:23:55 -0500 Subject: [PATCH 13/39] add wb_back on all task switch paths (#60617) Since this task's stack or scope field could have been modified after it was marked by an incremental collection (and not just for copy stacks), move the barrier back unconditionally here. --------- Co-authored-by: Valentin Churavy Co-authored-by: Jeff Bezanson (cherry picked from commit 14ca1abc7237fc586d71d8b1b2c5a2d08bc2276e) --- src/task.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/task.c b/src/task.c index 019a301b1f062..735af58b8fd35 100644 --- a/src/task.c +++ b/src/task.c @@ -203,10 +203,6 @@ static void NOINLINE save_stack(jl_ptls_t ptls, jl_task_t *lastt, jl_task_t **pt lastt->ctx.copy_stack = nb; lastt->sticky = 1; memcpy_stack_a16((uint64_t*)buf, (uint64_t*)frame_addr, nb); - // this task's stack could have been modified after - // it was marked by an incremental collection - // move the barrier back instead of walking it again here - jl_gc_wb_back(lastt); } JL_NO_ASAN static void NOINLINE JL_NORETURN restore_stack(jl_ucontext_t *t, jl_ptls_t ptls, char *p) @@ -504,6 +500,12 @@ JL_NO_ASAN static void ctx_switch(jl_task_t *lastt) lastt->ctx.ctx = &lasttstate.ctx; } } + // this task's stack or scope field could have been modified after + // it was marked by an incremental collection + // move the barrier back instead of walking the shadow stack again here to check if that is required + // even if killed (dropping the stack) and just the scope field matters, + // let the gc figure that out next time it does a quick mark + jl_gc_wb_back(lastt); // set up global state for new task and clear global state for old task t->ptls = ptls; From 3c92c840c8ff839586a44c1f1e24483bcdabfe21 Mon Sep 17 00:00:00 2001 From: "William S. Moses" Date: Wed, 14 Jan 2026 21:45:30 -0600 Subject: [PATCH 14/39] [LLVMAllocOpt] Preserve metadata lowering an alloca (cherry picked from commit 346b4e1cb6dedc520cd7b16430326d1dd303163f) --- src/llvm-alloc-opt.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/llvm-alloc-opt.cpp b/src/llvm-alloc-opt.cpp index ce1d22f42d0ae..7005d4329c9fc 100644 --- a/src/llvm-alloc-opt.cpp +++ b/src/llvm-alloc-opt.cpp @@ -701,6 +701,7 @@ void Optimizer::moveToStack(CallInst *orig_inst, size_t sz, bool has_ref, AllocF initializeAlloca(builder, buff, allockind); } Instruction *new_inst = cast(ptr); + new_inst->copyMetadata(*orig_inst); new_inst->takeName(orig_inst); auto simple_replace = [&] (Instruction *orig_i, Instruction *new_i) { From 03eeb808609d35d1ab25f19153aee916371777a8 Mon Sep 17 00:00:00 2001 From: DilumAluthgeBot <43731525+DilumAluthgeBot@users.noreply.github.com> Date: Fri, 16 Jan 2026 05:13:42 -0500 Subject: [PATCH 15/39] =?UTF-8?q?=F0=9F=A4=96=20[backports-release-1.12]?= =?UTF-8?q?=20Bump=20SparseArrays=20stdlib=20f81a30d=20=E2=86=92=202376bf8?= =?UTF-8?q?=20(#60703)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Stdlib: SparseArrays URL: https://github.com/JuliaSparse/SparseArrays.jl.git Stdlib branch: release-1.12 Julia branch: backports-release-1.12 Old commit: f81a30d New commit: 2376bf8 Julia version: 1.12.4 SparseArrays version: 1.12.0 (Does not match) Bump invoked by: @dkarrasch Powered by: [BumpStdlibs.jl](https://github.com/JuliaLang/BumpStdlibs.jl) Diff: https://github.com/JuliaSparse/SparseArrays.jl/compare/f81a30d962b03d4048b26439d60979673e343b67...2376bf8734754c5dd4264186299ae6839ab7783f ``` $ git log --oneline f81a30d..2376bf8 2376bf8 Backports to v1.12.5 (#669) e545b39 Update CI workflow for Julia and checkout action (#659) e129aea Separate out tests for the GPL build and do not run them when GPL libs are not present (#658) ``` Co-authored-by: dkarrasch <26658441+dkarrasch@users.noreply.github.com> --- .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 - .../sha512 | 1 - stdlib/SparseArrays.version | 2 +- 5 files changed, 3 insertions(+), 3 deletions(-) create mode 100644 deps/checksums/SparseArrays-2376bf8734754c5dd4264186299ae6839ab7783f.tar.gz/md5 create mode 100644 deps/checksums/SparseArrays-2376bf8734754c5dd4264186299ae6839ab7783f.tar.gz/sha512 delete mode 100644 deps/checksums/SparseArrays-f81a30d962b03d4048b26439d60979673e343b67.tar.gz/md5 delete mode 100644 deps/checksums/SparseArrays-f81a30d962b03d4048b26439d60979673e343b67.tar.gz/sha512 diff --git a/deps/checksums/SparseArrays-2376bf8734754c5dd4264186299ae6839ab7783f.tar.gz/md5 b/deps/checksums/SparseArrays-2376bf8734754c5dd4264186299ae6839ab7783f.tar.gz/md5 new file mode 100644 index 0000000000000..233004ebc84fe --- /dev/null +++ b/deps/checksums/SparseArrays-2376bf8734754c5dd4264186299ae6839ab7783f.tar.gz/md5 @@ -0,0 +1 @@ +059aef1395d4160561236167379af7ae diff --git a/deps/checksums/SparseArrays-2376bf8734754c5dd4264186299ae6839ab7783f.tar.gz/sha512 b/deps/checksums/SparseArrays-2376bf8734754c5dd4264186299ae6839ab7783f.tar.gz/sha512 new file mode 100644 index 0000000000000..ea773676bed87 --- /dev/null +++ b/deps/checksums/SparseArrays-2376bf8734754c5dd4264186299ae6839ab7783f.tar.gz/sha512 @@ -0,0 +1 @@ +69015dbc54d0d374b89eea2464eafd0aae461ad819965ad5745fc5ce50cf9519eb4d4024209184a90f2704873a96a0fd3586bf354a036c4c44452141d5535f43 diff --git a/deps/checksums/SparseArrays-f81a30d962b03d4048b26439d60979673e343b67.tar.gz/md5 b/deps/checksums/SparseArrays-f81a30d962b03d4048b26439d60979673e343b67.tar.gz/md5 deleted file mode 100644 index 372c4735d1c07..0000000000000 --- a/deps/checksums/SparseArrays-f81a30d962b03d4048b26439d60979673e343b67.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -230a4d9544d2cec7c4adf9a50d228345 diff --git a/deps/checksums/SparseArrays-f81a30d962b03d4048b26439d60979673e343b67.tar.gz/sha512 b/deps/checksums/SparseArrays-f81a30d962b03d4048b26439d60979673e343b67.tar.gz/sha512 deleted file mode 100644 index 6f1838c5bb750..0000000000000 --- a/deps/checksums/SparseArrays-f81a30d962b03d4048b26439d60979673e343b67.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -d66a3b6e032af45b78f71d8c5851c3e6fe1c5159f4c4428b64f1f6a52a111a488a9cec8f702a9e5de96593f5d4735f7490ae6c33eee7155b31f743b82aebd38b diff --git a/stdlib/SparseArrays.version b/stdlib/SparseArrays.version index 98763ae71e777..0e282a70c0acb 100644 --- a/stdlib/SparseArrays.version +++ b/stdlib/SparseArrays.version @@ -1,4 +1,4 @@ SPARSEARRAYS_BRANCH = release-1.12 -SPARSEARRAYS_SHA1 = f81a30d962b03d4048b26439d60979673e343b67 +SPARSEARRAYS_SHA1 = 2376bf8734754c5dd4264186299ae6839ab7783f SPARSEARRAYS_GIT_URL := https://github.com/JuliaSparse/SparseArrays.jl.git SPARSEARRAYS_TAR_URL = https://api.github.com/repos/JuliaSparse/SparseArrays.jl/tarball/$1 From d6a0cd2009d6fa81c0739bb89b2a3ae38e2d6d14 Mon Sep 17 00:00:00 2001 From: Gabriel Baraldi Date: Fri, 16 Jan 2026 10:23:52 -0300 Subject: [PATCH 16/39] Remove potential unitialized memory in the GC stack (#60651) (cherry picked from commit f9d461f4669d44dea86bed497b9498054d998988) --- src/codegen.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/codegen.cpp b/src/codegen.cpp index 8f0644c0cff7f..67d2d134f3ec0 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -2305,6 +2305,19 @@ static inline jl_cgval_t value_to_pointer(jl_codectx_t &ctx, const jl_cgval_t &v Align align(julia_alignment(v.typ)); Type *ty = julia_type_to_llvm(ctx, v.typ); AllocaInst *loc = emit_static_alloca(ctx, ty, align); + jl_datatype_t *dt = (jl_datatype_t *)v.typ; + size_t npointers = dt->layout->first_ptr >= 0 ? dt->layout->npointers : 0; + if (npointers > 0) { + auto InsertPoint = ctx.builder.saveIP(); + ctx.builder.SetInsertPoint(ctx.topalloca->getParent(), ++ctx.topalloca->getIterator()); + for (size_t i = 0; i < npointers; i++) { + // make sure these are nullptr early from LLVM's perspective, in case it decides to SROA it + Value *ptr_field = emit_ptrgep(ctx, loc, jl_ptr_offset(dt, i) * sizeof(void *)); + ctx.builder.CreateAlignedStore( + Constant::getNullValue(ctx.types().T_prjlvalue), ptr_field, Align(sizeof(void *))); + } + ctx.builder.restoreIP(InsertPoint); + } auto tbaa = v.V == nullptr ? ctx.tbaa().tbaa_gcframe : ctx.tbaa().tbaa_stack; auto stack_ai = jl_aliasinfo_t::fromTBAA(ctx, tbaa); recombine_value(ctx, v, loc, stack_ai, align, false); From a7ec7a2b18bad28340175575ae8adaf69c5c19e7 Mon Sep 17 00:00:00 2001 From: Dilum Aluthge Date: Fri, 16 Jan 2026 13:25:31 -0500 Subject: [PATCH 17/39] Print `full_name` after precompiling extension (#60456) (cherry picked from commit 3690b1e09306e146718002db9b96edfb01253c72) Note: This cherry-pick required manual conflict-resolution. --- base/precompilation.jl | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/base/precompilation.jl b/base/precompilation.jl index 45634d877115f..27c380a3d14af 100644 --- a/base/precompilation.jl +++ b/base/precompilation.jl @@ -845,7 +845,7 @@ function _precompilepkgs(pkgs::Union{Vector{String}, Vector{PkgId}}, write(get!(IOBuffer, std_outputs, pkg_config), str) if !in(pkg_config, taskwaiting) && occursin("waiting for IO to finish", str) !fancyprint && @lock print_lock begin - println(io, pkg.name, color_string(" Waiting for background task / IO / timer.", Base.warn_color())) + println(io, full_name(ext_to_parent, pkg), color_string(" Waiting for background task / IO / timer.", Base.warn_color())) end push!(taskwaiting, pkg_config) end @@ -1015,7 +1015,8 @@ function _precompilepkgs(pkgs::Union{Vector{String}, Vector{PkgId}}, try # allows processes to wait if another process is precompiling a given package to # a functionally identical package cache (except for preferences, which may differ) - t = @elapsed ret = precompile_pkgs_maybe_cachefile_lock(io, print_lock, fancyprint, pkg_config, pkgspidlocked, hascolor, parallel_limiter, ignore_loaded) do + fullname = full_name(ext_to_parent, pkg) + t = @elapsed ret = precompile_pkgs_maybe_cachefile_lock(io, print_lock, fancyprint, pkg_config, pkgspidlocked, hascolor, parallel_limiter, ignore_loaded, fullname) do Base.with_logger(Base.NullLogger()) do # whether to respect already loaded dependency versions keep_loaded_modules = !ignore_loaded @@ -1094,7 +1095,7 @@ function _precompilepkgs(pkgs::Union{Vector{String}, Vector{PkgId}}, str = sprint(context=io) do iostr if !quick_exit if fancyprint # replace the progress bar - what = isempty(requested_pkgids) ? "packages finished." : "$(join((p.name for p in requested_pkgids), ", ", " and ")) finished." + what = isempty(requested_pkgids) ? "packages finished." : "$(join((full_name(ext_to_parent, p) for p in requested_pkgids), ", ", " and ")) finished." printpkgstyle(iostr, :Precompiling, what) end plural = length(configs) > 1 ? "dependency configurations" : ndeps == 1 ? "dependency" : "dependencies" @@ -1157,7 +1158,7 @@ function _precompilepkgs(pkgs::Union{Vector{String}, Vector{PkgId}}, for (pkg_config, err) in failed_deps dep, config = pkg_config if strict || (dep in project_deps) - print(err_str, "\n", dep.name, " ") + print(err_str, "\n", full_name(ext_to_parent, dep), " ") for cfg in config[1] print(err_str, cfg, " ") end @@ -1205,7 +1206,7 @@ function _color_string(cstr::String, col::Union{Int64, Symbol}, hascolor) end # Can be merged with `maybe_cachefile_lock` in loading? -function precompile_pkgs_maybe_cachefile_lock(f, io::IO, print_lock::ReentrantLock, fancyprint::Bool, pkg_config, pkgspidlocked, hascolor, parallel_limiter::Base.Semaphore, ignore_loaded::Bool) +function precompile_pkgs_maybe_cachefile_lock(f, io::IO, print_lock::ReentrantLock, fancyprint::Bool, pkg_config, pkgspidlocked, hascolor, parallel_limiter::Base.Semaphore, ignore_loaded::Bool, fullname) if !(isdefined(Base, :mkpidlock_hook) && isdefined(Base, :trymkpidlock_hook) && Base.isdefined(Base, :parse_pidfile_hook)) return f() end @@ -1226,7 +1227,7 @@ function precompile_pkgs_maybe_cachefile_lock(f, io::IO, print_lock::ReentrantLo "another machine (hostname: $hostname, pid: $pid, pidfile: $pidfile)" end !fancyprint && @lock print_lock begin - println(io, " ", pkg.name, _color_string(" Being precompiled by $(pkgspidlocked[pkg_config])", Base.info_color(), hascolor)) + println(io, " ", fullname, _color_string(" Being precompiled by $(pkgspidlocked[pkg_config])", Base.info_color(), hascolor)) end Base.release(parallel_limiter) # release so other work can be done while waiting try From 30e745e53d439cdd4ae6d80eef7b50f37a4b567f Mon Sep 17 00:00:00 2001 From: Gabriel Baraldi Date: Tue, 16 Dec 2025 17:00:31 -0300 Subject: [PATCH 18/39] Add heap sized based full gc heuristic (#60376) (cherry picked from commit 14945c0a717693b077ea555e7b1d8aa5b03ec61a) --- base/timing.jl | 2 +- src/gc-stock.c | 42 ++++++++++++++++++++++++++---------------- src/gc-stock.h | 3 ++- 3 files changed, 29 insertions(+), 18 deletions(-) diff --git a/base/timing.jl b/base/timing.jl index 230849c7eb3d7..98f4f06cbcd47 100644 --- a/base/timing.jl +++ b/base/timing.jl @@ -113,7 +113,7 @@ end @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] + :FULL_SWEEP_REASON_USER_MAX_EXCEEDED, :FULL_SWEEP_REASON_LARGE_PROMOTION_RATE, :FULL_SWEEP_REASON_LARGE_HEAP_GROWTH] end """ diff --git a/src/gc-stock.c b/src/gc-stock.c index 52759a2febc61..a95dc5f731a5b 100644 --- a/src/gc-stock.c +++ b/src/gc-stock.c @@ -197,6 +197,7 @@ static int mark_reset_age = 0; static int64_t scanned_bytes; // young bytes scanned while marking static int64_t perm_scanned_bytes; // old bytes scanned while marking +static int64_t heap_size_after_last_full_gc = 0; int prev_sweep_full = 1; int current_sweep_full = 0; int next_sweep_full = 0; @@ -2232,13 +2233,13 @@ JL_DLLEXPORT void jl_gc_mark_queue_objarray(jl_ptls_t ptls, jl_value_t *parent, // `_new_obj` has its lowest bit tagged if it's in the remset (in which case we shouldn't update page metadata) FORCE_INLINE void gc_mark_outrefs(jl_ptls_t ptls, jl_gc_markqueue_t *mq, void *_new_obj) { - int meta_updated = (uintptr_t)_new_obj & GC_REMSET_PTR_TAG; + int remset_object = (uintptr_t)_new_obj & GC_REMSET_PTR_TAG; jl_value_t *new_obj = (jl_value_t *)((uintptr_t)_new_obj & ~(uintptr_t)GC_REMSET_PTR_TAG); mark_obj: { jl_taggedvalue_t *o = jl_astaggedvalue(new_obj); uintptr_t vtag = o->header & ~(uintptr_t)0xf; uint8_t bits = (gc_old(o->header) && !mark_reset_age) ? GC_OLD_MARKED : GC_MARKED; - int update_meta = __likely(!meta_updated && !gc_verifying); + int update_meta = __likely(!remset_object && !gc_verifying); int foreign_alloc = 0; if (update_meta && o->bits.in_image) { foreign_alloc = 1; @@ -2338,7 +2339,7 @@ FORCE_INLINE void gc_mark_outrefs(jl_ptls_t ptls, jl_gc_markqueue_t *mq, void *_ uintptr_t nptr = (npointers << 2) | 1 | bits; new_obj = gc_mark_obj8(ptls, obj8_parent, obj8_begin, obj8_end, nptr); if (new_obj != NULL) { - if (!meta_updated) + if (!remset_object) goto mark_obj; else gc_ptr_queue_push(mq, new_obj); @@ -2460,7 +2461,7 @@ FORCE_INLINE void gc_mark_outrefs(jl_ptls_t ptls, jl_gc_markqueue_t *mq, void *_ assert(obj8_begin < obj8_end); new_obj = gc_mark_obj8(ptls, obj8_parent, obj8_begin, obj8_end, nptr); if (new_obj != NULL) { - if (!meta_updated) + if (!remset_object) goto mark_obj; else gc_ptr_queue_push(mq, new_obj); @@ -2473,7 +2474,7 @@ FORCE_INLINE void gc_mark_outrefs(jl_ptls_t ptls, jl_gc_markqueue_t *mq, void *_ assert(obj16_begin < obj16_end); new_obj = gc_mark_obj16(ptls, obj16_parent, obj16_begin, obj16_end, nptr); if (new_obj != NULL) { - if (!meta_updated) + if (!remset_object) goto mark_obj; else gc_ptr_queue_push(mq, new_obj); @@ -2488,7 +2489,7 @@ FORCE_INLINE void gc_mark_outrefs(jl_ptls_t ptls, jl_gc_markqueue_t *mq, void *_ assert(obj32_begin < obj32_end); new_obj = gc_mark_obj32(ptls, obj32_parent, obj32_begin, obj32_end, nptr); if (new_obj != NULL) { - if (!meta_updated) + if (!remset_object) goto mark_obj; else gc_ptr_queue_push(mq, new_obj); @@ -3036,7 +3037,6 @@ static int _jl_gc_collect(jl_ptls_t ptls, jl_gc_collection_t collection) uint64_t gc_start_time = jl_hrtime(); uint64_t mutator_time = gc_end_time == 0 ? old_mut_time : gc_start_time - gc_end_time; uint64_t before_free_heap_size = jl_atomic_load_relaxed(&gc_heap_stats.heap_size); - int64_t last_perm_scanned_bytes = perm_scanned_bytes; uint64_t start_mark_time = jl_hrtime(); JL_PROBE_GC_MARK_BEGIN(); { @@ -3130,8 +3130,9 @@ static int _jl_gc_collect(jl_ptls_t ptls, jl_gc_collection_t collection) gc_stats_all_pool(); gc_stats_big_obj(); gc_num.total_allocd += gc_num.allocd; - if (!prev_sweep_full) - promoted_bytes += perm_scanned_bytes - last_perm_scanned_bytes; + // promoted_bytes are all the new bytes scanned that got promoted to old but that have never seen a full GC as old + promoted_bytes += scanned_bytes; + scanned_bytes = 0; // 4. next collection decision int remset_nptr = 0; int sweep_full = next_sweep_full; @@ -3157,13 +3158,6 @@ static int _jl_gc_collect(jl_ptls_t ptls, jl_gc_collection_t collection) recollect = 1; gc_count_full_sweep_reason(FULL_SWEEP_REASON_FORCED_FULL_SWEEP); } - if (sweep_full) { - // these are the difference between the number of gc-perm bytes scanned - // on the first collection after sweep_full, and the current scan - perm_scanned_bytes = 0; - promoted_bytes = 0; - } - scanned_bytes = 0; // 5. start sweeping uint64_t start_sweep_time = jl_hrtime(); JL_PROBE_GC_SWEEP_BEGIN(sweep_full); @@ -3295,7 +3289,19 @@ static int _jl_gc_collect(jl_ptls_t ptls, jl_gc_collection_t collection) target_heap = jl_atomic_load_relaxed(&gc_heap_stats.heap_target); } + if (sweep_full) { + // these are the difference between the number of gc-perm bytes scanned + // on the first collection after sweep_full, and the current scan + perm_scanned_bytes = 0; + promoted_bytes = 0; + heap_size_after_last_full_gc = jl_atomic_load_relaxed(&gc_heap_stats.heap_size); + } + // We want to trigger full GCs either if the heap size has grown a lot since the last full GC. + // For this we use the overallocation function to see what a reasonable rate of growth is, + // or if there is too much memory that has not seen a full GC after being promoted to old. double old_ratio = (double)promoted_bytes/(double)heap_size; + double expected_heap_size = overallocation(heap_size_after_last_full_gc, 0, UINT64_MAX) + heap_size_after_last_full_gc; + double last_full_gc_heap_ratio = (double)heap_size/expected_heap_size; if (heap_size > user_max) { next_sweep_full = 1; gc_count_full_sweep_reason(FULL_SWEEP_REASON_USER_MAX_EXCEEDED); @@ -3304,6 +3310,10 @@ static int _jl_gc_collect(jl_ptls_t ptls, jl_gc_collection_t collection) next_sweep_full = 1; gc_count_full_sweep_reason(FULL_SWEEP_REASON_LARGE_PROMOTION_RATE); } + else if (last_full_gc_heap_ratio > 1) { + next_sweep_full = 1; + gc_count_full_sweep_reason(FULL_SWEEP_REASON_LARGE_HEAP_GROWTH); + } else { next_sweep_full = 0; } diff --git a/src/gc-stock.h b/src/gc-stock.h index d478ee1366da0..3abd42ea1bfd3 100644 --- a/src/gc-stock.h +++ b/src/gc-stock.h @@ -483,7 +483,8 @@ FORCE_INLINE void gc_big_object_link(bigval_t *sentinel_node, bigval_t *node) JL #define FULL_SWEEP_REASON_FORCED_FULL_SWEEP (1) #define FULL_SWEEP_REASON_USER_MAX_EXCEEDED (2) #define FULL_SWEEP_REASON_LARGE_PROMOTION_RATE (3) -#define FULL_SWEEP_NUM_REASONS (4) +#define FULL_SWEEP_REASON_LARGE_HEAP_GROWTH (4) +#define FULL_SWEEP_NUM_REASONS (5) extern JL_DLLEXPORT uint64_t jl_full_sweep_reasons[FULL_SWEEP_NUM_REASONS]; STATIC_INLINE void gc_count_full_sweep_reason(int reason) JL_NOTSAFEPOINT From 951ca465eb0d2557b4cadfedb5a48b70fd2e7e61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20G=C3=B6ttgens?= Date: Mon, 19 Jan 2026 18:26:42 +0100 Subject: [PATCH 19/39] [release-1.12] Fix JET warnings in `show(::IOBuffer, ::StackFrame)` (#60740) This backports the part of https://github.com/JuliaLang/julia/pull/60645 that is applicable to 1.12. Furthermore, I added a type assertion two lines above the other changes that was taken from https://github.com/JuliaLang/julia/pull/58430. Without this, there are still JET warnings in 1.12 for the above query. cc @DilumAluthge --- base/stacktraces.jl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/base/stacktraces.jl b/base/stacktraces.jl index 34af576737b33..3c1d5892e138c 100644 --- a/base/stacktraces.jl +++ b/base/stacktraces.jl @@ -273,9 +273,10 @@ function show_spec_linfo(io::IO, frame::StackFrame) if linfo isa Union{MethodInstance, CodeInstance} def = frame_method_or_module(frame) if def isa Module - Base.show_mi(io, linfo, #=from_stackframe=#true) + Base.show_mi(io, linfo::MethodInstance, #=from_stackframe=#true) else - show_spec_sig(io, def, frame_mi(frame).specTypes) + mi = frame_mi(frame)::MethodInstance + show_spec_sig(io, def::Method, mi.specTypes) end else m = linfo::Method From 348463142d6106153c73d0235538c769fdcf1307 Mon Sep 17 00:00:00 2001 From: James Wrigley Date: Mon, 19 Jan 2026 19:50:21 +0100 Subject: [PATCH 20/39] [JuliaSyntax] Bump version for 1.12 (#60641) This is to get https://github.com/JuliaLang/JuliaSyntax.jl/pull/616 in. Part of https://github.com/JuliaLang/julia/pull/60612. --- deps/JuliaSyntax.version | 4 ++-- .../md5 | 1 - .../sha512 | 1 - .../md5 | 1 + .../sha512 | 1 + 5 files changed, 4 insertions(+), 4 deletions(-) delete mode 100644 deps/checksums/JuliaSyntax-46723f071d5b2efcb21ca6757788028afb91cc13.tar.gz/md5 delete mode 100644 deps/checksums/JuliaSyntax-46723f071d5b2efcb21ca6757788028afb91cc13.tar.gz/sha512 create mode 100644 deps/checksums/JuliaSyntax-de4d1cd21cf13501c65b91c68c2fd5c9aa704e97.tar.gz/md5 create mode 100644 deps/checksums/JuliaSyntax-de4d1cd21cf13501c65b91c68c2fd5c9aa704e97.tar.gz/sha512 diff --git a/deps/JuliaSyntax.version b/deps/JuliaSyntax.version index 9487754d8a617..4372237a6824f 100644 --- a/deps/JuliaSyntax.version +++ b/deps/JuliaSyntax.version @@ -1,4 +1,4 @@ -JULIASYNTAX_BRANCH = main -JULIASYNTAX_SHA1 = 46723f071d5b2efcb21ca6757788028afb91cc13 +JULIASYNTAX_BRANCH = backports-julialang-1.12 +JULIASYNTAX_SHA1 = de4d1cd21cf13501c65b91c68c2fd5c9aa704e97 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-46723f071d5b2efcb21ca6757788028afb91cc13.tar.gz/md5 b/deps/checksums/JuliaSyntax-46723f071d5b2efcb21ca6757788028afb91cc13.tar.gz/md5 deleted file mode 100644 index ff40f520dfe85..0000000000000 --- a/deps/checksums/JuliaSyntax-46723f071d5b2efcb21ca6757788028afb91cc13.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -2a0921e59edfab54554aa173f091c5b7 diff --git a/deps/checksums/JuliaSyntax-46723f071d5b2efcb21ca6757788028afb91cc13.tar.gz/sha512 b/deps/checksums/JuliaSyntax-46723f071d5b2efcb21ca6757788028afb91cc13.tar.gz/sha512 deleted file mode 100644 index 64e90d0edaba0..0000000000000 --- a/deps/checksums/JuliaSyntax-46723f071d5b2efcb21ca6757788028afb91cc13.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -17050e23216335f6599f009f71e9614a11b6686e455554b1efd287cd8526a7ebece06dc473e34cd50f61bf52085ff72bb4279144a9fdb3a234d3d589a10fddaf diff --git a/deps/checksums/JuliaSyntax-de4d1cd21cf13501c65b91c68c2fd5c9aa704e97.tar.gz/md5 b/deps/checksums/JuliaSyntax-de4d1cd21cf13501c65b91c68c2fd5c9aa704e97.tar.gz/md5 new file mode 100644 index 0000000000000..5daa6f8f8538f --- /dev/null +++ b/deps/checksums/JuliaSyntax-de4d1cd21cf13501c65b91c68c2fd5c9aa704e97.tar.gz/md5 @@ -0,0 +1 @@ +420f41ff75161d7ee4e3a564228de206 diff --git a/deps/checksums/JuliaSyntax-de4d1cd21cf13501c65b91c68c2fd5c9aa704e97.tar.gz/sha512 b/deps/checksums/JuliaSyntax-de4d1cd21cf13501c65b91c68c2fd5c9aa704e97.tar.gz/sha512 new file mode 100644 index 0000000000000..2fa1d2693fd44 --- /dev/null +++ b/deps/checksums/JuliaSyntax-de4d1cd21cf13501c65b91c68c2fd5c9aa704e97.tar.gz/sha512 @@ -0,0 +1 @@ +fbc2cc1b2dd1903c15e33d86a4f1f84807cd7988cd169c62fea58a71af06694deb5d3882a772ab9599ee878bdaa51e069e5a399e565f03b0eb2c99c2b5ec4759 From b8fbf4966c513a9f1b20e0a9b7b163c68706ed69 Mon Sep 17 00:00:00 2001 From: Gabriel Baraldi Date: Mon, 19 Jan 2026 17:33:37 -0300 Subject: [PATCH 21/39] [1.12] Reland "Add getter for get llvm codeinstances" (#60738) Co-authored-by: William Moses --- src/aotcompile.cpp | 18 ++++++++++++++++++ src/codegen-stubs.c | 4 ++-- src/jl_exported_funcs.inc | 1 + src/julia_internal.h | 3 ++- src/staticdata.c | 2 +- 5 files changed, 24 insertions(+), 4 deletions(-) diff --git a/src/aotcompile.cpp b/src/aotcompile.cpp index df8a825df3ffe..9a1157afda3e4 100644 --- a/src/aotcompile.cpp +++ b/src/aotcompile.cpp @@ -108,6 +108,24 @@ jl_get_llvm_mis_impl(void *native_code, size_t *num_elements, jl_method_instance } } +extern "C" JL_DLLEXPORT_CODEGEN void +jl_get_llvm_cis_impl(void *native_code, size_t *num_elements, jl_code_instance_t **data) +{ + jl_native_code_desc_t *desc = (jl_native_code_desc_t *)native_code; + auto &map = desc->jl_fvar_map; + + if (data == NULL) { + *num_elements = map.size(); + return; + } + + assert(*num_elements == map.size()); + size_t i = 0; + for (auto &ci : map) { + data[i++] = ci.first; + } +} + extern "C" JL_DLLEXPORT_CODEGEN void jl_get_llvm_gvs_impl(void *native_code, size_t *num_elements, void **data) { diff --git a/src/codegen-stubs.c b/src/codegen-stubs.c index e35fdfddfbab9..7c89ec8d5809c 100644 --- a/src/codegen-stubs.c +++ b/src/codegen-stubs.c @@ -16,8 +16,8 @@ JL_DLLEXPORT void jl_dump_native_fallback(void *native_code, JL_DLLEXPORT void jl_get_llvm_gvs_fallback(void *native_code, arraylist_t *gvs) UNAVAILABLE JL_DLLEXPORT void jl_get_llvm_gvs_globals_fallback(void *native_code, arraylist_t *gvs) UNAVAILABLE JL_DLLEXPORT void jl_get_llvm_external_fns_fallback(void *native_code, arraylist_t *gvs) UNAVAILABLE -JL_DLLEXPORT void jl_get_llvm_mis_fallback(void *native_code, arraylist_t* MIs) UNAVAILABLE - +JL_DLLEXPORT void jl_get_llvm_mis_fallback(void *native_code, size_t *num_elements, jl_method_instance_t **data) UNAVAILABLE +JL_DLLEXPORT void jl_get_llvm_cis_fallback(void *native_code, size_t *num_elements, jl_code_instance_t **data) UNAVAILABLE JL_DLLEXPORT jl_value_t *jl_dump_method_asm_fallback(jl_method_instance_t *linfo, size_t world, char emit_mc, char getwrapper, const char* asm_variant, const char *debuginfo, char binary) UNAVAILABLE JL_DLLEXPORT jl_value_t *jl_dump_function_ir_fallback(jl_llvmf_dump_t *dump, char strip_ir_metadata, char dump_module, const char *debuginfo) UNAVAILABLE diff --git a/src/jl_exported_funcs.inc b/src/jl_exported_funcs.inc index 8d2c1d391cb3c..9e3a14aa6cb96 100644 --- a/src/jl_exported_funcs.inc +++ b/src/jl_exported_funcs.inc @@ -526,6 +526,7 @@ YY(jl_get_llvm_gvs_globals) \ YY(jl_get_llvm_external_fns) \ YY(jl_get_llvm_mis) \ + YY(jl_get_llvm_cis) \ YY(jl_dump_function_asm) \ YY(jl_LLVMCreateDisasm) \ YY(jl_LLVMDisasmInstruction) \ diff --git a/src/julia_internal.h b/src/julia_internal.h index a74fcfb7903d3..0d3d45f35501f 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -2065,7 +2065,8 @@ JL_DLLIMPORT void jl_get_function_id(void *native_code, jl_code_instance_t *ncod JL_DLLIMPORT void jl_register_fptrs(uint64_t image_base, const struct _jl_image_fptrs_t *fptrs, jl_method_instance_t **linfos, size_t n); JL_DLLIMPORT void jl_get_llvm_mis(void *native_code, size_t *num_els, - jl_method_instance_t *MIs); + jl_method_instance_t **MIs); +JL_DLLIMPORT void jl_get_llvm_cis(void *native_code, size_t *num_els, jl_code_instance_t **data); JL_DLLIMPORT void jl_init_codegen(void); JL_DLLIMPORT void jl_teardown_codegen(void) JL_NOTSAFEPOINT; JL_DLLIMPORT int jl_getFunctionInfo(jl_frame_t **frames, uintptr_t pointer, int skipC, int noInline) JL_NOTSAFEPOINT; diff --git a/src/staticdata.c b/src/staticdata.c index 466bb349fa4e1..20a265550b612 100644 --- a/src/staticdata.c +++ b/src/staticdata.c @@ -3108,7 +3108,7 @@ static void jl_save_system_image_to_stream(ios_t *f, jl_array_t *mod_array, size_t num_mis; jl_get_llvm_mis(native_functions, &num_mis, NULL); arraylist_grow(&MIs, num_mis); - jl_get_llvm_mis(native_functions, &num_mis, (jl_method_instance_t*)MIs.items); + jl_get_llvm_mis(native_functions, &num_mis, (jl_method_instance_t**)MIs.items); } } if (jl_options.trim) { From 10ccaaf8ad5990f9e4a75404075d6c1e5ad9226c Mon Sep 17 00:00:00 2001 From: William Moses Date: Mon, 19 Jan 2026 21:42:40 -0500 Subject: [PATCH 22/39] [1.12] Reland "Enable getting non-boxed LLVM type from Julia Type" (#60743) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit …#59492)" This reverts commit cfcd7d8a327457e4e121457e103989f0185d9dc8. Backport of https://github.com/JuliaLang/julia/pull/60739 --- src/cgutils.cpp | 15 +++++++++++---- src/codegen-stubs.c | 2 ++ src/codegen.cpp | 4 ++-- src/jl_exported_funcs.inc | 1 + 4 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/cgutils.cpp b/src/cgutils.cpp index 65a01c1355f76..cc8ea597516a3 100644 --- a/src/cgutils.cpp +++ b/src/cgutils.cpp @@ -628,13 +628,13 @@ static unsigned convert_struct_offset(jl_codectx_t &ctx, Type *lty, unsigned byt static Type *_julia_struct_to_llvm(jl_codegen_params_t *ctx, LLVMContext &ctxt, jl_value_t *jt, bool *isboxed, bool llvmcall=false); -static Type *_julia_type_to_llvm(jl_codegen_params_t *ctx, LLVMContext &ctxt, jl_value_t *jt, bool *isboxed) +static Type *_julia_type_to_llvm(jl_codegen_params_t *ctx, LLVMContext &ctxt, jl_value_t *jt, bool *isboxed, bool no_boxing) { // this function converts a Julia Type into the equivalent LLVM type if (isboxed) *isboxed = false; if (jt == (jl_value_t*)jl_bottom_type || jt == (jl_value_t*)jl_typeofbottom_type || jt == (jl_value_t*)jl_typeofbottom_type->super) return getVoidTy(ctxt); - if (jl_is_concrete_immutable(jt)) { + if (jl_is_concrete_immutable(jt) || no_boxing) { if (jl_datatype_nbits(jt) == 0) return getVoidTy(ctxt); Type *t = _julia_struct_to_llvm(ctx, ctxt, jt, isboxed); @@ -647,13 +647,20 @@ static Type *_julia_type_to_llvm(jl_codegen_params_t *ctx, LLVMContext &ctxt, jl static Type *julia_type_to_llvm(jl_codectx_t &ctx, jl_value_t *jt, bool *isboxed) { - return _julia_type_to_llvm(&ctx.emission_context, ctx.builder.getContext(), jt, isboxed); + return _julia_type_to_llvm(&ctx.emission_context, ctx.builder.getContext(), jt, isboxed, false); } extern "C" JL_DLLEXPORT_CODEGEN Type *jl_type_to_llvm_impl(jl_value_t *jt, LLVMContextRef ctxt, bool *isboxed) { - return _julia_type_to_llvm(NULL, *unwrap(ctxt), jt, isboxed); + return _julia_type_to_llvm(NULL, *unwrap(ctxt), jt, isboxed, false); +} + + +extern "C" JL_DLLEXPORT_CODEGEN +Type *jl_struct_to_llvm_impl(jl_value_t *jt, LLVMContextRef ctxt, bool *isboxed) +{ + return _julia_type_to_llvm(NULL, *unwrap(ctxt), jt, isboxed, true); } diff --git a/src/codegen-stubs.c b/src/codegen-stubs.c index 7c89ec8d5809c..c77c449fa1b3d 100644 --- a/src/codegen-stubs.c +++ b/src/codegen-stubs.c @@ -117,6 +117,8 @@ JL_DLLEXPORT LLVMOrcThreadSafeModuleRef jl_get_llvm_module_fallback(void *native JL_DLLEXPORT void *jl_type_to_llvm_fallback(jl_value_t *jt, LLVMContextRef llvmctxt, bool_t *isboxed) UNAVAILABLE +JL_DLLEXPORT void *jl_struct_to_llvm_fallback(jl_value_t *jt, LLVMContextRef llvmctxt, bool_t *isboxed) UNAVAILABLE + JL_DLLEXPORT jl_value_t *jl_get_libllvm_fallback(void) JL_NOTSAFEPOINT { return jl_nothing; diff --git a/src/codegen.cpp b/src/codegen.cpp index 67d2d134f3ec0..506e99582ad4d 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -7868,7 +7868,7 @@ static jl_returninfo_t get_specsig_function(jl_codegen_params_t ¶ms, Module } else if (!deserves_retbox(jlrettype)) { bool retboxed; - rt = _julia_type_to_llvm(¶ms, M->getContext(), jlrettype, &retboxed); + rt = _julia_type_to_llvm(¶ms, M->getContext(), jlrettype, &retboxed, /*noboxing*/false); assert(!retboxed); if (rt != getVoidTy(M->getContext()) && deserves_sret(jlrettype, rt)) { auto tracked = CountTrackedPointers(rt, true); @@ -7944,7 +7944,7 @@ static jl_returninfo_t get_specsig_function(jl_codegen_params_t ¶ms, Module if (is_uniquerep_Type(jt)) continue; isboxed = deserves_argbox(jt); - et = isboxed ? T_prjlvalue : _julia_type_to_llvm(¶ms, M->getContext(), jt, nullptr); + et = isboxed ? T_prjlvalue : _julia_type_to_llvm(¶ms, M->getContext(), jt, nullptr, /*noboxing*/false); if (type_is_ghost(et)) continue; } diff --git a/src/jl_exported_funcs.inc b/src/jl_exported_funcs.inc index 9e3a14aa6cb96..4f2b5b7cfdd8f 100644 --- a/src/jl_exported_funcs.inc +++ b/src/jl_exported_funcs.inc @@ -544,6 +544,7 @@ YY(jl_dump_fptr_asm) \ YY(jl_emit_native) \ YY(jl_get_function_id) \ + YY(jl_struct_to_llvm) \ YY(jl_type_to_llvm) \ YY(jl_getUnwindInfo) \ YY(jl_get_libllvm) \ From e2ff74f837b3442c1493157b705024e2bd506114 Mon Sep 17 00:00:00 2001 From: William Moses Date: Mon, 19 Jan 2026 21:43:06 -0500 Subject: [PATCH 23/39] =?UTF-8?q?[1.12]=20LateLowerGCFrame:=20More=20relev?= =?UTF-8?q?ant=20diagnostic=20information=20on=20non-=E2=80=A6=20(#60742)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit …alloca sret --- src/llvm-late-gc-lowering.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/llvm-late-gc-lowering.cpp b/src/llvm-late-gc-lowering.cpp index 1d262ff7968b0..b5ad4358626dc 100644 --- a/src/llvm-late-gc-lowering.cpp +++ b/src/llvm-late-gc-lowering.cpp @@ -1208,6 +1208,13 @@ State LateLowerGCFrame::LocalScan(Function &F) { auto tracked = CountTrackedPointers(ElT, true); if (tracked.count) { AllocaInst *SRet = dyn_cast((CI->arg_begin()[0])->stripInBoundsOffsets()); + if (!SRet) { + llvm::errs() << "LLVMLateGCLowering: Expected AllocaInst, found" << *(CI->arg_begin()[0])->stripInBoundsOffsets() << "\n"; + llvm::errs() << " + CI: " << *CI << "\n"; + llvm::errs() << " + scope: " << *CI->getFunction() << "\n"; + llvm::errs() << " + callee: " << *CI->getCalledOperand() << "\n"; + llvm_unreachable("LLVMLateGCLowering: Expected AllocaInst"); + } assert(SRet); { if (!(SRet->isStaticAlloca() && isa(ElT) && ElT->getPointerAddressSpace() == AddressSpace::Tracked)) { From ce066ed6df8debdfc2711bae05f7da7fdb20cac5 Mon Sep 17 00:00:00 2001 From: Valentin Churavy Date: Wed, 14 Jan 2026 14:40:12 +0100 Subject: [PATCH 24/39] Preserve the scope across the exception handler (#60647) We store the previous scope in the eh_state and thus hide it from GC. This means we need to manually preserve that scope across the `try ... catch`, instead fo the new scope that we switch to. --------- Co-authored-by: Nathan Daly Co-authored-by: Keno Fischer (cherry picked from commit f22ae77a82ec9adb1182566bc1c2be3a58f0fca2) --- src/codegen.cpp | 9 +++++---- src/gc-interface.h | 7 +++++++ src/interpreter.c | 12 ++++++------ src/rtutils.c | 2 ++ src/task.c | 2 ++ test/scopedvalues.jl | 43 +++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 65 insertions(+), 10 deletions(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index 506e99582ad4d..979034b0a1ca5 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -6172,6 +6172,7 @@ static void emit_stmtpos(jl_codectx_t &ctx, jl_value_t *expr, int ssaval_result) Value *scope_ptr = get_scope_field(ctx); jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_gcframe).decorateInst( ctx.builder.CreateAlignedStore(scope_to_restore, scope_ptr, ctx.types().alignof_ptr)); + // NOTE: wb not needed here, due to store to current_task (see jl_gc_wb_current_task) } } else if (head == jl_pop_exception_sym) { @@ -9347,12 +9348,12 @@ static jl_llvm_functions_t Value *scope_ptr = get_scope_field(ctx); LoadInst *current_scope = ctx.builder.CreateAlignedLoad(ctx.types().T_prjlvalue, scope_ptr, ctx.types().alignof_ptr); StoreInst *scope_store = ctx.builder.CreateAlignedStore(scope_boxed, scope_ptr, ctx.types().alignof_ptr); + // NOTE: wb not needed here, due to store to current_task (see jl_gc_wb_current_task) jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_gcframe).decorateInst(current_scope); jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_gcframe).decorateInst(scope_store); - // 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 - Value *scope_token = ctx.builder.CreateCall(prepare_call(gc_preserve_begin_func), {scope_boxed}); + // GC preserve the current_scope, since it is not rooted in the `jl_handler_t *`, + // the newly entered scope is preserved through the current_task. + Value *scope_token = ctx.builder.CreateCall(prepare_call(gc_preserve_begin_func), {current_scope}); ctx.scope_restore[cursor] = std::make_pair(scope_token, current_scope); } } diff --git a/src/gc-interface.h b/src/gc-interface.h index 826e91355b17a..c67b7720e70a5 100644 --- a/src/gc-interface.h +++ b/src/gc-interface.h @@ -241,6 +241,13 @@ STATIC_INLINE void jl_gc_wb(const void *parent, const void *ptr) JL_NOTSAFEPOINT // can be used to annotate that a write barrier would be required were it not for this property // (as opposed to somebody just having forgotten to think about write barriers). STATIC_INLINE void jl_gc_wb_fresh(const void *parent, const void *ptr) JL_NOTSAFEPOINT {} +// As an optimization, the current_task is explicitly added to the remset while it is running. +// Upon deschedule, we conservatively move the write barrier into the young generation. +// This allows the omission of write barriers for all GC roots on the current task stack (JL_GC_PUSH_*), +// as well as the Task's explicit fields (but only for the current task). +// This function is a no-op that can be used to annotate that a write barrier would be required were +// it not for this property (as opposed to somebody just having forgotten to think about write barriers). +STATIC_INLINE void jl_gc_wb_current_task(const void *parent JL_UNUSED, const void *ptr JL_UNUSED) JL_NOTSAFEPOINT {} // Used to annotate that a write barrier would be required, but may be omitted because `ptr` // is known to be an old object. STATIC_INLINE void jl_gc_wb_knownold(const void *parent, const void *ptr) JL_NOTSAFEPOINT {} diff --git a/src/interpreter.c b/src/interpreter.c index a692cadaf9be1..55f8ec9188b19 100644 --- a/src/interpreter.c +++ b/src/interpreter.c @@ -539,12 +539,12 @@ 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; + jl_value_t *old_scope = ct->scope; // Identical to __eh.scope + // GC preserve the old_scope, since it is not rooted in the `jl_handler_t *`, + // the newly entered scope is preserved through the current_task. + JL_GC_PUSH1(&old_scope); + ct->scope = eval_value(jl_enternode_scope(stmt), s); + jl_gc_wb_current_task(ct, ct->scope); if (!jl_setjmp(__eh.eh_ctx, 0)) { ct->eh = &__eh; eval_body(stmts, s, next_ip, toplevel); diff --git a/src/rtutils.c b/src/rtutils.c index 6d4a375017bf2..77ce3c0d4adb0 100644 --- a/src/rtutils.c +++ b/src/rtutils.c @@ -296,6 +296,7 @@ JL_DLLEXPORT void jl_eh_restore_state(jl_task_t *ct, jl_handler_t *eh) ct->eh = eh->prev; ct->gcstack = eh->gcstack; ct->scope = eh->scope; + jl_gc_wb_current_task(ct, ct->scope); small_arraylist_t *locks = &ptls->locks; int unlocks = locks->len > eh->locks_len; if (unlocks) { @@ -335,6 +336,7 @@ JL_DLLEXPORT void jl_eh_restore_state_noexcept(jl_task_t *ct, jl_handler_t *eh) { assert(ct->gcstack == eh->gcstack && "Incorrect GC usage under try catch"); ct->scope = eh->scope; + jl_gc_wb_current_task(ct, ct->scope); ct->eh = eh->prev; ct->ptls->defer_signal = eh->defer_signal; // optional, but certain try-finally (in stream.jl) may be slightly harder to write without this } diff --git a/src/task.c b/src/task.c index 735af58b8fd35..bb86bce48101a 100644 --- a/src/task.c +++ b/src/task.c @@ -1110,6 +1110,7 @@ JL_DLLEXPORT jl_task_t *jl_new_task(jl_function_t *start, jl_value_t *completion jl_atomic_store_relaxed(&t->_isexception, 0); // Inherit scope from parent task t->scope = ct->scope; + jl_gc_wb_fresh(t, t->scope); // Fork task-local random state from parent jl_rng_split(t->rngState, ct->rngState); // there is no active exception handler available on this stack yet @@ -1575,6 +1576,7 @@ jl_task_t *jl_init_root_task(jl_ptls_t ptls, void *stack_lo, void *stack_hi) ct->donenotify = jl_nothing; jl_atomic_store_relaxed(&ct->_isexception, 0); ct->scope = jl_nothing; + jl_gc_wb_knownold(ct, ct->scope); ct->eh = NULL; ct->gcstack = NULL; ct->excstack = NULL; diff --git a/test/scopedvalues.jl b/test/scopedvalues.jl index e9b36d80fc2c4..20f709f755915 100644 --- a/test/scopedvalues.jl +++ b/test/scopedvalues.jl @@ -195,3 +195,46 @@ nothrow_scope() push!(ts, 2) end end + +using Base.ScopedValues: ScopedValue, with +@noinline function test_59483() + sv = ScopedValue([]) + ch = Channel{Bool}() + + # Spawn a child task, which inherits the parent's Scope + @noinline function inner_function() + # Block until the parent task has left the scope. + take!(ch) + # Now, per issue 59483, this task's scope is not rooted, except by the task itself. + + # Now switch to an inner scope, leaving the current scope possibly unrooted. + val = with(sv=>Any[2]) do + # Inside this new scope, when we perform GC, the parent scope can be freed. + # The fix for this issue made sure that the first scope in this task remains + # rooted. + GC.gc() + GC.gc() + sv[] + end + @test val == Any[2] + # Finally, we've returned to the original scope, but that could be a dangling + # pointer if the scope itself was freed by the above GCs. So these GCs could crash: + GC.gc() + GC.gc() + end + @noinline function spawn_inner() + # Set a new Scope in the parent task - this is the scope that could be freed. + with(sv=>Any[1]) do + return @async inner_function() + end + end + + # RUN THE TEST: + t = spawn_inner() + # Exit the scope, and let the child task proceed + put!(ch, true) + wait(t) +end +@testset "issue 59483" begin + test_59483() +end From bf91f309fc795308001390e51ea19a252659306d Mon Sep 17 00:00:00 2001 From: Tim Besard Date: Tue, 20 Jan 2026 19:19:35 +0100 Subject: [PATCH 25/39] [1.12] Backport #60718+#60746: Fix and test `jl_method_lookup_by_tt` (#60749) --- src/gf.c | 12 ++++++------ test/core.jl | 26 ++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/src/gf.c b/src/gf.c index 47db5474701ac..9dcca5637be09 100644 --- a/src/gf.c +++ b/src/gf.c @@ -1841,7 +1841,7 @@ JL_DLLEXPORT jl_typemap_entry_t *jl_mt_find_cache_entry(jl_methcache_t *mc JL_PR return entry; } -static jl_method_instance_t *jl_mt_assoc_by_type(jl_methcache_t *mc JL_PROPAGATES_ROOT, jl_datatype_t *tt JL_MAYBE_UNROOTED, size_t world) +static jl_method_instance_t *jl_mt_assoc_by_type(jl_methtable_t *mt, jl_methcache_t *mc JL_PROPAGATES_ROOT, jl_datatype_t *tt JL_MAYBE_UNROOTED, size_t world) { jl_typemap_entry_t *entry = jl_mt_find_cache_entry(mc, tt, world); if (entry) @@ -1858,11 +1858,11 @@ static jl_method_instance_t *jl_mt_assoc_by_type(jl_methcache_t *mc JL_PROPAGATE if (!mi) { size_t min_valid = 0; size_t max_valid = ~(size_t)0; - matc = _gf_invoke_lookup((jl_value_t*)tt, jl_method_table, world, 0, &min_valid, &max_valid); + matc = _gf_invoke_lookup((jl_value_t*)tt, mt, world, 0, &min_valid, &max_valid); if (matc) { jl_method_t *m = matc->method; jl_svec_t *env = matc->sparams; - mi = cache_method(jl_method_table, mc, &mc->cache, (jl_value_t*)mc, tt, m, world, min_valid, max_valid, env); + mi = cache_method(mt, mc, &mc->cache, (jl_value_t*)mc, tt, m, world, min_valid, max_valid, env); JL_GC_POP(); return mi; } @@ -3079,7 +3079,7 @@ JL_DLLEXPORT jl_value_t *jl_method_lookup_by_tt(jl_tupletype_t *tt, size_t world mt = (jl_methtable_t*) _mt; } jl_methcache_t *mc = mt->cache; - jl_method_instance_t *mi = jl_mt_assoc_by_type(mc, tt, world); + jl_method_instance_t *mi = jl_mt_assoc_by_type(mt, mc, tt, world); if (!mi) return jl_nothing; return (jl_value_t*) mi; @@ -3094,7 +3094,7 @@ JL_DLLEXPORT jl_method_instance_t *jl_method_lookup(jl_value_t **args, size_t na if (entry) return entry->func.linfo; jl_tupletype_t *tt = arg_type_tuple(args[0], &args[1], nargs); - return jl_mt_assoc_by_type(mc, tt, world); + return jl_mt_assoc_by_type(jl_method_table, mc, tt, world); } // return a Vector{Any} of svecs, each describing a method match: @@ -4169,7 +4169,7 @@ STATIC_INLINE jl_method_instance_t *jl_lookup_generic_(jl_value_t *F, jl_value_t assert(tt); // cache miss case jl_methcache_t *mc = jl_method_table->cache; - mfunc = jl_mt_assoc_by_type(mc, tt, world); + mfunc = jl_mt_assoc_by_type(jl_method_table, mc, tt, world); if (jl_options.malloc_log) jl_gc_sync_total_bytes(last_alloc); // discard allocation count from compilation if (mfunc == NULL) { diff --git a/test/core.jl b/test/core.jl index d18f7fe8136b6..d85a4f3c2eb74 100644 --- a/test/core.jl +++ b/test/core.jl @@ -8422,6 +8422,32 @@ let ms = Base._methods_by_ftype(Tuple{typeof(sin), Int}, OverlayModule.mt, 1, Ba @test isempty(ms) end +# fresh module to ensure uncached methods +module OverlayMTTest + using Base.Experimental: @MethodTable, @overlay + @MethodTable(mt) + + function overlay_only end + @overlay mt overlay_only(x::Int) = x * 2 +end + +# #60702 & #60716: Overlay methods must be found without prior cache population +let world = Base.get_world_counter() + mi = Base.method_instance(OverlayMTTest.overlay_only, Tuple{Int}; + world, method_table=OverlayMTTest.mt) + @test mi isa Core.MethodInstance + @test mi.def.module === OverlayMTTest +end + +# #60712: Global-only methods must NOT be found via custom MT +let + @eval global_only_func(x::Int) = x + 1 + world = Base.get_world_counter() + mi = Base.method_instance(global_only_func, Tuple{Int}; + world, method_table=OverlayMTTest.mt) + @test mi === nothing +end + # precompilation let load_path = mktempdir() depot_path = mkdepottempdir() From 13b377ad7188b045bc90a394da07feafa97b0845 Mon Sep 17 00:00:00 2001 From: Gabriel Baraldi Date: Tue, 20 Jan 2026 17:09:31 -0300 Subject: [PATCH 26/39] [1.12] Don't skip CIs from external absints (#60741) Should fix one of the GPUCompiler caching issues --------- Co-authored-by: Valentin Churavy Co-authored-by: Dilum Aluthge --- src/staticdata_utils.c | 2 +- test/precompile_absint1.jl | 30 +++++++++++------------------- test/precompile_absint2.jl | 37 +++++++++++++------------------------ test/precompile_utils.jl | 11 +++++++++++ 4 files changed, 36 insertions(+), 44 deletions(-) diff --git a/src/staticdata_utils.c b/src/staticdata_utils.c index 1d8ed0e19b04b..0dc64d27a57a9 100644 --- a/src/staticdata_utils.c +++ b/src/staticdata_utils.c @@ -314,7 +314,7 @@ static jl_array_t *queue_external_cis(jl_array_t *list, jl_query_cache *query_ca assert(jl_is_code_instance(ci)); jl_method_instance_t *mi = jl_get_ci_mi(ci); jl_method_t *m = mi->def.method; - if (ci->owner == jl_nothing && jl_atomic_load_relaxed(&ci->inferred) && jl_is_method(m) && jl_object_in_image((jl_value_t*)m->module)) { + if (jl_atomic_load_relaxed(&ci->inferred) && jl_is_method(m) && jl_object_in_image((jl_value_t*)m->module)) { int found = has_backedge_to_worklist(mi, &visited, &stack, query_cache); assert(found == 0 || found == 1 || found == 2); assert(stack.len == 0); diff --git a/test/precompile_absint1.jl b/test/precompile_absint1.jl index 98078ebf41098..000ba116bae95 100644 --- a/test/precompile_absint1.jl +++ b/test/precompile_absint1.jl @@ -43,43 +43,35 @@ precompile_test_harness() do load_path let m = only(methods(TestAbsIntPrecompile1.basic_callee)) mi = only(Base.specializations(m)) ci = mi.cache - @test_broken isdefined(ci, :next) + ci = check_presence(mi, nothing) + @test ci !== nothing @test ci.owner === nothing @test ci.max_world == typemax(UInt) @test Base.module_build_id(TestAbsIntPrecompile1) == Base.object_build_id(ci) - @test_skip begin - ci = ci.next - @test !isdefined(ci, :next) + ci = check_presence(mi, cache_owner) + @test ci !== nothing @test ci.owner === cache_owner @test ci.max_world == typemax(UInt) @test Base.module_build_id(TestAbsIntPrecompile1) == Base.object_build_id(ci) - end end let m = only(methods(sum, (Vector{Float64},))) - found = false for mi in Base.specializations(m) if mi isa Core.MethodInstance && mi.specTypes == Tuple{typeof(sum),Vector{Float64}} - ci = mi.cache - @test_broken isdefined(ci, :next) - @test_broken ci.owner === cache_owner - @test_skip begin - @test ci.max_world == typemax(UInt) - @test Base.module_build_id(TestAbsIntPrecompile1) == - Base.object_build_id(ci) - ci = ci.next - end - @test !isdefined(ci, :next) + ci = check_presence(mi, nothing) + @test ci !== nothing @test ci.owner === nothing @test ci.max_world == typemax(UInt) + @test Base.module_build_id(TestAbsIntPrecompile1) == Base.object_build_id(ci) + ci = check_presence(mi, cache_owner) + @test ci !== nothing + @test ci.owner === cache_owner + @test ci.max_world == typemax(UInt) @test Base.module_build_id(TestAbsIntPrecompile1) == Base.object_build_id(ci) - found = true - break end end - @test found end end end diff --git a/test/precompile_absint2.jl b/test/precompile_absint2.jl index 4aa84e0992f7c..5b0ba32a8df82 100644 --- a/test/precompile_absint2.jl +++ b/test/precompile_absint2.jl @@ -62,44 +62,33 @@ precompile_test_harness() do load_path TestAbsIntPrecompile2.Custom.PrecompileInterpreter()) let m = only(methods(TestAbsIntPrecompile2.basic_callee)) mi = only(Base.specializations(m)) - ci = mi.cache - @test_broken isdefined(ci, :next) + ci = check_presence(mi, nothing) + @test ci !== nothing @test ci.owner === nothing @test ci.max_world == typemax(UInt) @test Base.module_build_id(TestAbsIntPrecompile2) == Base.object_build_id(ci) - @test_skip begin - ci = ci.next - @test !isdefined(ci, :next) + ci = check_presence(mi, cache_owner) + @test ci !== nothing @test ci.owner === cache_owner @test ci.max_world == typemax(UInt) - @test Base.module_build_id(TestAbsIntPrecompile2) == - Base.object_build_id(ci) - end + @test Base.module_build_id(TestAbsIntPrecompile2) == Base.object_build_id(ci) end let m = only(methods(sum, (Vector{Float64},))) - found = false for mi = Base.specializations(m) if mi isa Core.MethodInstance && mi.specTypes == Tuple{typeof(sum),Vector{Float64}} - ci = mi.cache - @test_broken isdefined(ci, :next) - @test_broken ci.owner === cache_owner - @test_skip begin - @test ci.max_world == typemax(UInt) - @test Base.module_build_id(TestAbsIntPrecompile2) == - Base.object_build_id(ci) - ci = ci.next - end - @test !isdefined(ci, :next) + ci = check_presence(mi, nothing) + @test ci !== nothing @test ci.owner === nothing @test ci.max_world == typemax(UInt) - @test Base.module_build_id(TestAbsIntPrecompile2) == - Base.object_build_id(ci) - found = true - break + @test Base.module_build_id(TestAbsIntPrecompile2) == Base.object_build_id(ci) + ci = check_presence(mi, cache_owner) + @test ci !== nothing + @test ci.owner === cache_owner + @test ci.max_world == typemax(UInt) + @test Base.module_build_id(TestAbsIntPrecompile2) == Base.object_build_id(ci) end end - @test found end end end diff --git a/test/precompile_utils.jl b/test/precompile_utils.jl index c9a7c98d262e0..685161e98840f 100644 --- a/test/precompile_utils.jl +++ b/test/precompile_utils.jl @@ -29,3 +29,14 @@ let original_depot_path = copy(Base.DEPOT_PATH) append!(Base.LOAD_PATH, original_load_path) end end + +function check_presence(mi, token) + ci = isdefined(mi, :cache) ? mi.cache : nothing + while ci !== nothing + if ci.owner === token && ci.max_world == typemax(UInt) + return ci + end + ci = isdefined(ci, :next) ? ci.next : nothing + end + return nothing +end From 0d0c031a46f429570034219c5eb9e9014155d7ae Mon Sep 17 00:00:00 2001 From: Cody Tapscott <84105208+topolarity@users.noreply.github.com> Date: Wed, 14 Jan 2026 15:49:45 -0500 Subject: [PATCH 27/39] codegen: Use `emit_static_roots` for "allroots" case (#60656) The way this ABI optimization is implemented is a bit awkward, since it means that either the "bytes" have to start acting like "pointers" or vice-versa. This adds some minimal plumbing to make sure that we label these as roots appropriately (and zero-init them). --- src/codegen.cpp | 7 ++++++- src/jitlayers.h | 1 + 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index 979034b0a1ca5..ce3f10bbae9b5 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -5020,7 +5020,11 @@ static jl_cgval_t emit_call_specfun_other(jl_codectx_t &ctx, bool is_opaque_clos AllocaInst *result = nullptr; if (returninfo.cc == jl_returninfo_t::SRet || returninfo.cc == jl_returninfo_t::Union) { - result = emit_static_alloca(ctx, returninfo.union_bytes, Align(returninfo.union_align)); + if (returninfo.all_roots) { + result = emit_static_roots(ctx, returninfo.union_bytes / sizeof(void *)); + } else { + result = emit_static_alloca(ctx, returninfo.union_bytes, Align(returninfo.union_align)); + } setName(ctx.emission_context, result, "sret_box"); argvals[idx] = result; idx++; @@ -7881,6 +7885,7 @@ static jl_returninfo_t get_specsig_function(jl_codegen_params_t ¶ms, Module props.cc = jl_returninfo_t::SRet; props.union_bytes = jl_datatype_size(jlrettype); props.union_align = props.union_minalign = julia_alignment(jlrettype); + props.all_roots = tracked.all; // sret is always passed from alloca assert(M); fsig.push_back(rt->getPointerTo(M->getDataLayout().getAllocaAddrSpace())); diff --git a/src/jitlayers.h b/src/jitlayers.h index 070bf576ffd35..7975d4c4163a7 100644 --- a/src/jitlayers.h +++ b/src/jitlayers.h @@ -203,6 +203,7 @@ struct jl_returninfo_t { size_t union_align; size_t union_minalign; unsigned return_roots; + bool all_roots; }; struct jl_codegen_call_target_t { From b8e84ae2b53fc6d7038aea587a0e81e2c8e23370 Mon Sep 17 00:00:00 2001 From: Tim Besard Date: Sat, 24 Jan 2026 11:15:40 +0100 Subject: [PATCH 28/39] [1.12] Fix `codeinfo_for_const` missing `nargs` and `isva` fields (#60787) Backport a fix from #59413 to unbreak `Compiler.inflate_ir!` with const-return IR. --- Compiler/src/typeinfer.jl | 2 ++ Compiler/test/ssair.jl | 12 ++++++++++++ 2 files changed, 14 insertions(+) diff --git a/Compiler/src/typeinfer.jl b/Compiler/src/typeinfer.jl index 746b32c22506c..060072e834f2c 100644 --- a/Compiler/src/typeinfer.jl +++ b/Compiler/src/typeinfer.jl @@ -1052,6 +1052,8 @@ function codeinfo_for_const(interp::AbstractInterpreter, mi::MethodInstance, @no tree.ssaflags = [IR_FLAG_NULL] tree.rettype = Core.Typeof(val) tree.edges = Core.svec() + tree.nargs = UInt(nargs) + tree.isva = method.isva set_inlineable!(tree, true) tree.parent = mi return tree diff --git a/Compiler/test/ssair.jl b/Compiler/test/ssair.jl index 80faac97d83a4..f7ceda07601e2 100644 --- a/Compiler/test/ssair.jl +++ b/Compiler/test/ssair.jl @@ -838,3 +838,15 @@ end let ir = Base.code_ircode(_worker_task57153, (), optimize_until="CC: COMPACT_2")[1].first @test findfirst(x->x==0, ir.cfg.blocks[1].preds) !== nothing end + +# codeinfo_for_const should set nargs and isva +let + _const_return_func(@nospecialize(x)) = 42 + mi = Compiler.specialize_method(only(methods(_const_return_func)), Tuple{typeof(_const_return_func), Int}, Core.svec()) + ci = Compiler.codeinfo_for_const(Compiler.NativeInterpreter(), mi, 42) + @test ci.nargs == 2 + @test ci.isva == false + # inflate_ir! should succeed now that nargs/isva are set + ir = Compiler.inflate_ir!(ci, mi) + @test ir isa Compiler.IRCode +end From 3d40701b4f020162c49284c911ca23b62765e586 Mon Sep 17 00:00:00 2001 From: Gabriel Baraldi Date: Wed, 21 Jan 2026 02:44:19 -0300 Subject: [PATCH 29/39] llvm: Fix alloca alignment and type selection in AllocOpt (#60699) Inherit alignment from the original GC allocation with JL_SMALL_BYTE_ALIGNMENT as the minimum. Use alignment-sized integer chunks for the alloca type (matching emit_static_alloca) so SROA splits allocations into aligned pieces for better performance and vectorization. Also adds the missing setAlignment call in splitOnStack. Co-Authored-By: Claude Opus 4.5 --------- Co-authored-by: Claude Opus 4.5 (cherry picked from commit 54fde7e012e6883a5bc9acd964593c8b9c9b5c74) --- src/codegen.cpp | 8 ++-- src/llvm-alloc-opt.cpp | 37 +++++++++------ .../alloc-opt-gcframe-addrspaces.ll | 2 +- test/llvmpasses/alloc-opt-gcframe.ll | 9 ++-- test/llvmpasses/alloc-opt-pass.ll | 45 ++++++++++++++++--- 5 files changed, 72 insertions(+), 29 deletions(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index ce3f10bbae9b5..816a4d24583a8 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -2163,9 +2163,11 @@ static AllocaInst *emit_static_alloca(jl_codectx_t &ctx, unsigned nb, Align alig // if it cannot find something better to do, which is terrible for performance. // However, if we emit this with an element size equal to the alignment, it will instead split it into aligned chunks // which is great for performance and vectorization. - if (alignTo(nb, align) == align.value()) // don't bother with making an array of length 1 - return emit_static_alloca(ctx, ctx.builder.getIntNTy(align.value() * 8), align); - return emit_static_alloca(ctx, ArrayType::get(ctx.builder.getIntNTy(align.value() * 8), alignTo(nb, align) / align.value()), align); + // Cap element size at 64 bits since not all backends support larger integers. + unsigned elsize = std::min(align.value(), (uint64_t)8); + if (alignTo(nb, elsize) == elsize) // don't bother with making an array of length 1 + return emit_static_alloca(ctx, ctx.builder.getIntNTy(elsize * 8), align); + return emit_static_alloca(ctx, ArrayType::get(ctx.builder.getIntNTy(elsize * 8), alignTo(nb, elsize) / elsize), align); } static AllocaInst *emit_static_roots(jl_codectx_t &ctx, unsigned nroots) diff --git a/src/llvm-alloc-opt.cpp b/src/llvm-alloc-opt.cpp index 7005d4329c9fc..97e881296d1be 100644 --- a/src/llvm-alloc-opt.cpp +++ b/src/llvm-alloc-opt.cpp @@ -662,11 +662,8 @@ void Optimizer::moveToStack(CallInst *orig_inst, size_t sz, bool has_ref, AllocF // The allocation does not escape or get used in a phi node so none of the derived // SSA from it are live when we run the allocation again. // It is now safe to promote the allocation to an entry block alloca. - size_t align = 1; - // TODO: This is overly conservative. May want to instead pass this as a - // parameter to the allocation function directly. - if (sz > 1) - align = MinAlign(JL_SMALL_BYTE_ALIGNMENT, NextPowerOf2(sz)); + // Inherit alignment from the original allocation, with GC alignment as minimum. + Align align(std::max((unsigned)orig_inst->getRetAlign().valueOrOne().value(), (unsigned)JL_SMALL_BYTE_ALIGNMENT)); // No debug info for prolog instructions IRBuilder<> prolog_builder(&F.getEntryBlock().front()); AllocaInst *buff; @@ -682,17 +679,21 @@ void Optimizer::moveToStack(CallInst *orig_inst, size_t sz, bool has_ref, AllocF const DataLayout &DL = F.getParent()->getDataLayout(); auto asize = ConstantInt::get(Type::getInt64Ty(prolog_builder.getContext()), sz / DL.getTypeAllocSize(pass.T_prjlvalue)); buff = prolog_builder.CreateAlloca(pass.T_prjlvalue, asize); - buff->setAlignment(Align(align)); + buff->setAlignment(align); ptr = cast(buff); } else { + // Use alignment-sized chunks so SROA splits the alloca into aligned pieces + // which is better for performance and vectorization (see emit_static_alloca). + // Cap element size at 64 bits since not all backends support larger integers. Type *buffty; - if (pass.DL->isLegalInteger(sz * 8)) - buffty = Type::getIntNTy(pass.getLLVMContext(), sz * 8); + unsigned elsize = std::min(align.value(), (uint64_t)8); + if (alignTo(sz, elsize) == elsize) + buffty = Type::getIntNTy(pass.getLLVMContext(), elsize * 8); else - buffty = ArrayType::get(Type::getInt8Ty(pass.getLLVMContext()), sz); + buffty = ArrayType::get(Type::getIntNTy(pass.getLLVMContext(), elsize * 8), alignTo(sz, elsize) / elsize); buff = prolog_builder.CreateAlloca(buffty); - buff->setAlignment(Align(align)); + buff->setAlignment(align); ptr = cast(buff); } insertLifetime(ptr, ConstantInt::get(Type::getInt64Ty(prolog_builder.getContext()), sz), orig_inst); @@ -959,6 +960,8 @@ void Optimizer::splitOnStack(CallInst *orig_inst) uint32_t size; }; SmallVector slots; + // Inherit alignment from the original allocation, with GC alignment as minimum. + Align align(std::max((unsigned)orig_inst->getRetAlign().valueOrOne().value(), (unsigned)JL_SMALL_BYTE_ALIGNMENT)); for (auto memop: use_info.memops) { auto offset = memop.first; auto &field = memop.second; @@ -974,12 +977,18 @@ void Optimizer::splitOnStack(CallInst *orig_inst) else if (field.elty && !field.multiloc) { allocty = field.elty; } - else if (pass.DL->isLegalInteger(field.size * 8)) { - allocty = Type::getIntNTy(pass.getLLVMContext(), field.size * 8); - } else { - allocty = ArrayType::get(Type::getInt8Ty(pass.getLLVMContext()), field.size); + else { + // Use alignment-sized chunks so SROA splits the alloca into aligned pieces + // which is better for performance and vectorization (see emit_static_alloca). + // Cap element size at 64 bits since not all backends support larger integers. + unsigned elsize = std::min(align.value(), (uint64_t)8); + if (alignTo(field.size, elsize) == elsize) + allocty = Type::getIntNTy(pass.getLLVMContext(), elsize * 8); + else + allocty = ArrayType::get(Type::getIntNTy(pass.getLLVMContext(), elsize * 8), alignTo(field.size, elsize) / elsize); } slot.slot = prolog_builder.CreateAlloca(allocty); + slot.slot->setAlignment(align); IRBuilder<> builder(orig_inst); insertLifetime(slot.slot, ConstantInt::get(Type::getInt64Ty(prolog_builder.getContext()), field.size), orig_inst); initializeAlloca(builder, slot.slot, use_info.allockind); diff --git a/test/llvmpasses/alloc-opt-gcframe-addrspaces.ll b/test/llvmpasses/alloc-opt-gcframe-addrspaces.ll index b96c9385e38eb..c66cb815ea8b9 100644 --- a/test/llvmpasses/alloc-opt-gcframe-addrspaces.ll +++ b/test/llvmpasses/alloc-opt-gcframe-addrspaces.ll @@ -16,7 +16,7 @@ declare {}* @julia.pointer_from_objref({} addrspace(11)*) ; CHECK-LABEL: @non_zero_addrspace -; OPAQUE: %var1 = alloca i32, align 8, addrspace(5) +; OPAQUE: %var1 = alloca i64, align 16, addrspace(5) ; OPAQUE: %1 = addrspacecast ptr addrspace(5) %var1 to ptr ; OPAQUE: call void @llvm.lifetime.start.p5(i64 4, ptr addrspace(5) %var1) diff --git a/test/llvmpasses/alloc-opt-gcframe.ll b/test/llvmpasses/alloc-opt-gcframe.ll index f53a4d5c01df7..44714b702d7bc 100644 --- a/test/llvmpasses/alloc-opt-gcframe.ll +++ b/test/llvmpasses/alloc-opt-gcframe.ll @@ -25,7 +25,8 @@ define {} addrspace(10)* @return_obj() { ; CHECK-LABEL: }{{$}} ; CHECK-LABEL: @return_load -; CHECK: alloca i64 +; When the element type is known (i64), splitOnStack preserves it +; CHECK: alloca i64, align 16 ; CHECK-NOT: @julia.gc_alloc_obj ; CHECK-NOT: @jl_gc_small_alloc ; OPAQUE: call void @llvm.lifetime.start{{.*}}(i64 8, ptr @@ -62,7 +63,7 @@ define void @ccall_obj(i8* %fptr) { ; CHECK-LABEL: }{{$}} ; CHECK-LABEL: @ccall_ptr -; CHECK: alloca i64 +; CHECK: alloca i64, align 16 ; OPAQUE: call ptr @julia.get_pgcstack() ; CHECK-NOT: @julia.gc_alloc_obj ; CHECK-NOT: @jl_gc_small_alloc @@ -105,7 +106,7 @@ define void @ccall_unknown_bundle(i8* %fptr) { ; CHECK-LABEL: }{{$}} ; CHECK-LABEL: @lifetime_branches -; CHECK: alloca i64 +; CHECK: alloca i64, align 16 ; OPAQUE: call ptr @julia.get_pgcstack() ; CHECK: L1: ; CHECK-NEXT: call void @llvm.lifetime.start{{.*}}(i64 8, @@ -166,7 +167,7 @@ define void @object_field({} addrspace(10)* %field) { ; CHECK-LABEL: }{{$}} ; CHECK-LABEL: @memcpy_opt -; CHECK: alloca [16 x i8], align 16 +; CHECK: alloca [2 x i64], align 16 ; OPAQUE: call ptr @julia.get_pgcstack() ; CHECK-NOT: @julia.gc_alloc_obj ; CHECK-NOT: @jl_gc_small_alloc diff --git a/test/llvmpasses/alloc-opt-pass.ll b/test/llvmpasses/alloc-opt-pass.ll index 83f2118412cc1..ad79baafd19dd 100644 --- a/test/llvmpasses/alloc-opt-pass.ll +++ b/test/llvmpasses/alloc-opt-pass.ll @@ -79,8 +79,9 @@ declare ptr addrspace(10) @external_function2() ; CHECK-LABEL: @legal_int_types -; CHECK: alloca [12 x i8] -; CHECK-NOT: alloca i96 +; Test that allocations use i64 chunks (capped at 64 bits for backend compatibility) +; A 12-byte allocation rounds up to 16 bytes, giving [2 x i64] +; CHECK: alloca [2 x i64], align 16 ; CHECK: call void @llvm.memset.p0.i64(ptr align 16 %var1, ; CHECK: ret void define void @legal_int_types() { @@ -151,11 +152,10 @@ define void @lifetime_no_preserve_end(ptr noalias nocapture noundef nonnull sret ; CHECK-LABEL: @initializers -; CHECK: alloca [1 x i8] -; CHECK-DAG: alloca [2 x i8] -; CHECK-DAG: alloca [3 x i8] -; CHECK-DAG: call void @llvm.memset.p0.i64(ptr align 1 %var1, -; CHECK-DAG: call void @llvm.memset.p0.i64(ptr align 4 %var7, +; Small allocations (1, 2, 3 bytes) all round up to 8 bytes, giving i64 +; CHECK-DAG: alloca i64, align 16 +; CHECK-DAG: call void @llvm.memset.p0.i64(ptr align 16 %var1, +; CHECK-DAG: call void @llvm.memset.p0.i64(ptr align 16 %var7, ; CHECK: ret void define void @initializers() { %pgcstack = call ptr @julia.get_pgcstack() @@ -268,6 +268,37 @@ define swiftcc i64 @"atomicrmw"(ptr nonnull swiftself %0) #0 { ret i64 %19 } +; Test that higher alignment from the original allocation is inherited +; 8 bytes with 32-byte alignment uses i64 (element size capped at 64 bits) +; CHECK-LABEL: @align_inherit +; CHECK: alloca i64, align 32 +; CHECK: ret void +define void @align_inherit() { + %pgcstack = call ptr @julia.get_pgcstack() + %ptls = call ptr @julia.ptls_states() + %ptls_i8 = bitcast ptr %ptls to ptr + %var1 = call align 32 ptr addrspace(10) @julia.gc_alloc_obj(ptr %ptls_i8, i64 8, ptr addrspace(10) @tag) + %var2 = addrspacecast ptr addrspace(10) %var1 to ptr addrspace(11) + %var3 = call ptr @julia.pointer_from_objref(ptr addrspace(11) %var2) + ret void +} +; CHECK-LABEL: }{{$}} + +; Test that 8-byte allocation uses i64 with GC alignment +; CHECK-LABEL: @legal_int_i64 +; CHECK: alloca i64, align 16 +; CHECK: ret void +define void @legal_int_i64() { + %pgcstack = call ptr @julia.get_pgcstack() + %ptls = call ptr @julia.ptls_states() + %ptls_i8 = bitcast ptr %ptls to ptr + %var1 = call ptr addrspace(10) @julia.gc_alloc_obj(ptr %ptls_i8, i64 8, ptr addrspace(10) @tag) + %var2 = addrspacecast ptr addrspace(10) %var1 to ptr addrspace(11) + %var3 = call ptr @julia.pointer_from_objref(ptr addrspace(11) %var2) + ret void +} +; CHECK-LABEL: }{{$}} + declare ptr @julia.ptls_states() declare ptr @julia.pointer_from_objref(ptr addrspace(11)) From 0f21d93eaa6ad75fb4be985b9f85c740be8755e9 Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Sat, 24 Jan 2026 10:32:55 -0500 Subject: [PATCH 30/39] bindings: Define behavior of `using` an ambiguous binding (#60804) We were missing handling for the case where the binding that we're using is ambiguous. There are two possible behaviors: 1. The ambiguous binding gets ignored for the purpose of resolution 2. The ambiguity poisons and the imported binding is also ambiguous Current behavior between these two depends on resolution order (which is bad and part of what the assert was complaining about). This decides that case #2 is the correct behavior and fixes #60659. (cherry picked from commit 0f275e3e8b43bed4062c33ddb615ae9aececd4c3) --- src/module.c | 5 ++++- test/core.jl | 14 ++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/module.c b/src/module.c index 60d2652ddfaaa..546a8d3e60fb8 100644 --- a/src/module.c +++ b/src/module.c @@ -104,7 +104,6 @@ static void update_implicit_resolution(struct implicit_search_resolution *to_upd return; } if (to_update->ultimate_kind == PARTITION_KIND_GUARD) { - assert(resolution.binding_or_const); to_update->ultimate_kind = resolution.ultimate_kind; to_update->binding_or_const = resolution.binding_or_const; to_update->debug_only_import_from = resolution.debug_only_import_from; @@ -310,6 +309,10 @@ struct implicit_search_resolution jl_resolve_implicit_import(jl_binding_t *b, mo imp_resolution.binding_or_const = tempbpart->restriction; imp_resolution.debug_only_ultimate_binding = tempb; imp_resolution.ultimate_kind = PARTITION_KIND_IMPLICIT_CONST; + } else if (kind == PARTITION_KIND_FAILED) { + imp_resolution.binding_or_const = NULL; + imp_resolution.debug_only_ultimate_binding = tempb; + imp_resolution.ultimate_kind = PARTITION_KIND_FAILED; } } imp_resolution.debug_only_import_from = imp; diff --git a/test/core.jl b/test/core.jl index d85a4f3c2eb74..dd2dfd86b9619 100644 --- a/test/core.jl +++ b/test/core.jl @@ -8631,3 +8631,17 @@ primitive type ByteString58434 (18 * 8) end @test Base.datatype_isbitsegal(Tuple{ByteString58434}) == false @test Base.datatype_haspadding(Tuple{ByteString58434}) == (length(Base.padding(Tuple{ByteString58434})) > 0) + +# #60659 - Behavior of using'd ambiguous bindings +module AmbiguousUsing60659 + using Test + module A + export X + module B; struct X; end; export X; end + module C; struct X; end; export X; end + using .B, .C + end + module D; struct X; end; export X; end + using .D, .A + @test_throws UndefVarError X +end From 164ac88aef0d33caf1755c9edef110e62b92edac Mon Sep 17 00:00:00 2001 From: Sam Schweigel Date: Tue, 20 Jan 2026 08:15:37 -0800 Subject: [PATCH 31/39] Temporarily reintroduce a global type inference lock (#60689) (cherry picked from commit 23a98ffd193a0946636976bcd92af8f69853f8b6) --- src/gf.c | 8 ++++++-- src/init.c | 2 ++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/gf.c b/src/gf.c index 9dcca5637be09..2c5abf7343d09 100644 --- a/src/gf.c +++ b/src/gf.c @@ -399,6 +399,8 @@ static jl_code_instance_t *jl_method_inferred_with_abi(jl_method_instance_t *mi return NULL; } +jl_mutex_t jl_typeinf_lock; + // run type inference on lambda "mi" for given argument types. // returns the inferred source, and may cache the result in mi // if successful, also updates the mi argument to describe the validity of this src @@ -427,6 +429,7 @@ jl_code_instance_t *jl_type_infer(jl_method_instance_t *mi, size_t world, uint8_ return NULL; JL_TIMING(INFERENCE, INFERENCE); jl_value_t **fargs; + JL_GC_PUSH1(&ci); JL_GC_PUSHARGS(fargs, 5); fargs[0] = (jl_value_t*)jl_typeinf_func; fargs[1] = (jl_value_t*)mi; @@ -458,6 +461,7 @@ jl_code_instance_t *jl_type_infer(jl_method_instance_t *mi, size_t world, uint8_ // increase that limit, we'll need to // allocate another bit for the counter. ct->reentrant_timing += 0b10; + JL_LOCK(&jl_typeinf_lock); JL_TRY { ci = (jl_code_instance_t*)jl_apply(fargs, 5); } @@ -481,6 +485,7 @@ jl_code_instance_t *jl_type_infer(jl_method_instance_t *mi, size_t world, uint8_ abort(); #endif } + JL_UNLOCK(&jl_typeinf_lock); ct->world_age = last_age; ct->reentrant_timing -= 0b10; ct->ptls->in_pure_callback = last_pure; @@ -495,12 +500,11 @@ jl_code_instance_t *jl_type_infer(jl_method_instance_t *mi, size_t world, uint8_ // Record inference entrance backtrace if enabled if (ci) { - JL_GC_PUSH1(&ci); jl_push_inference_entrance_backtraces((jl_value_t*)ci); - JL_GC_POP(); } JL_GC_POP(); + JL_GC_POP(); #endif return ci; diff --git a/src/init.c b/src/init.c index e11b360b5d378..f86435b3b9035 100644 --- a/src/init.c +++ b/src/init.c @@ -552,6 +552,7 @@ extern jl_mutex_t precomp_statement_out_lock; extern jl_mutex_t newly_inferred_mutex; extern jl_mutex_t global_roots_lock; extern jl_mutex_t profile_show_peek_cond_lock; +extern jl_mutex_t jl_typeinf_lock; static void restore_fp_env(void) { @@ -661,6 +662,7 @@ static void init_global_mutexes(void) { JL_MUTEX_INIT(&global_roots_lock, "global_roots_lock"); JL_MUTEX_INIT(&typecache_lock, "typecache_lock"); JL_MUTEX_INIT(&profile_show_peek_cond_lock, "profile_show_peek_cond_lock"); + JL_MUTEX_INIT(&jl_typeinf_lock, "jl_typeinf_lock"); } JL_DLLEXPORT void jl_init_(jl_image_buf_t sysimage) From a1391e5af89b1500ad0a81e061b18c8b06a7eaa7 Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Wed, 28 Jan 2026 14:58:13 +0100 Subject: [PATCH 32/39] bump Pkg to latest release-1.12 --- stdlib/Pkg.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/Pkg.version b/stdlib/Pkg.version index 853940e299a8e..56f673f582df6 100644 --- a/stdlib/Pkg.version +++ b/stdlib/Pkg.version @@ -1,4 +1,4 @@ PKG_BRANCH = release-1.12 -PKG_SHA1 = b322a8ff786472d680a972e57575b4586fefe018 +PKG_SHA1 = 1c1c173d35be3eb478054fb0229b0b1b6aa040cf PKG_GIT_URL := https://github.com/JuliaLang/Pkg.jl.git PKG_TAR_URL = https://api.github.com/repos/JuliaLang/Pkg.jl/tarball/$1 From 422f456051886227b11b6a65faecadd99b79fb71 Mon Sep 17 00:00:00 2001 From: Max Horn Date: Tue, 27 Jan 2026 22:43:12 +0100 Subject: [PATCH 33/39] Make `jl_reinit_foreign_type` idempotent even with asserts (#60827) That is, if it is called twice (e.g. because precompilation is disabled) with identical arguments, then this should not trigger an assertion. This should resolve https://github.com/oscar-system/GAP.jl/issues/1314 (cherry picked from commit 1708d99d98e1d0e5b3b277f74b6a10f2f675387d) --- src/datatype.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/datatype.c b/src/datatype.c index 4cf2c6d780bbd..447586135cdc7 100644 --- a/src/datatype.c +++ b/src/datatype.c @@ -1040,10 +1040,14 @@ JL_DLLEXPORT int jl_reinit_foreign_type(jl_datatype_t *dt, const jl_datatype_layout_t *layout = dt->layout; jl_fielddescdyn_t * desc = (jl_fielddescdyn_t *) ((char *)layout + sizeof(*layout)); - assert(!desc->markfunc); - assert(!desc->sweepfunc); - desc->markfunc = markfunc; - desc->sweepfunc = sweepfunc; + if (desc->markfunc != markfunc) { + assert(!desc->markfunc); + desc->markfunc = markfunc; + } + if (desc->sweepfunc != sweepfunc) { + assert(!desc->sweepfunc); + desc->sweepfunc = sweepfunc; + } return 1; } From fc453392ecd42b478cd156514576d4f44328adb1 Mon Sep 17 00:00:00 2001 From: Dilum Aluthge Date: Wed, 28 Jan 2026 21:38:33 -0500 Subject: [PATCH 34/39] `deps/checksums`: Remove old Pkg checksums, and add new Pkg checksums --- .../Pkg-1c1c173d35be3eb478054fb0229b0b1b6aa040cf.tar.gz/md5 | 1 + .../Pkg-1c1c173d35be3eb478054fb0229b0b1b6aa040cf.tar.gz/sha512 | 1 + .../Pkg-b322a8ff786472d680a972e57575b4586fefe018.tar.gz/md5 | 1 - .../Pkg-b322a8ff786472d680a972e57575b4586fefe018.tar.gz/sha512 | 1 - 4 files changed, 2 insertions(+), 2 deletions(-) create mode 100644 deps/checksums/Pkg-1c1c173d35be3eb478054fb0229b0b1b6aa040cf.tar.gz/md5 create mode 100644 deps/checksums/Pkg-1c1c173d35be3eb478054fb0229b0b1b6aa040cf.tar.gz/sha512 delete mode 100644 deps/checksums/Pkg-b322a8ff786472d680a972e57575b4586fefe018.tar.gz/md5 delete mode 100644 deps/checksums/Pkg-b322a8ff786472d680a972e57575b4586fefe018.tar.gz/sha512 diff --git a/deps/checksums/Pkg-1c1c173d35be3eb478054fb0229b0b1b6aa040cf.tar.gz/md5 b/deps/checksums/Pkg-1c1c173d35be3eb478054fb0229b0b1b6aa040cf.tar.gz/md5 new file mode 100644 index 0000000000000..21bc73b46a108 --- /dev/null +++ b/deps/checksums/Pkg-1c1c173d35be3eb478054fb0229b0b1b6aa040cf.tar.gz/md5 @@ -0,0 +1 @@ +8893f471072bfa55bf25077d3759e951 diff --git a/deps/checksums/Pkg-1c1c173d35be3eb478054fb0229b0b1b6aa040cf.tar.gz/sha512 b/deps/checksums/Pkg-1c1c173d35be3eb478054fb0229b0b1b6aa040cf.tar.gz/sha512 new file mode 100644 index 0000000000000..b9d18c5a172ed --- /dev/null +++ b/deps/checksums/Pkg-1c1c173d35be3eb478054fb0229b0b1b6aa040cf.tar.gz/sha512 @@ -0,0 +1 @@ +6df46485c91f099501a27cb8f28688df94afa7ad877b8d1ddf9b3d210cbd5ed6c8386cb92f2f2ff6a13b855de4755f9159e0d3b9699d638b5eaf4f16513c58d1 diff --git a/deps/checksums/Pkg-b322a8ff786472d680a972e57575b4586fefe018.tar.gz/md5 b/deps/checksums/Pkg-b322a8ff786472d680a972e57575b4586fefe018.tar.gz/md5 deleted file mode 100644 index fbc81b025a8f9..0000000000000 --- a/deps/checksums/Pkg-b322a8ff786472d680a972e57575b4586fefe018.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -9fbd2f2f522bf0b5fd0d6b40f722db49 diff --git a/deps/checksums/Pkg-b322a8ff786472d680a972e57575b4586fefe018.tar.gz/sha512 b/deps/checksums/Pkg-b322a8ff786472d680a972e57575b4586fefe018.tar.gz/sha512 deleted file mode 100644 index 576ad3c62915e..0000000000000 --- a/deps/checksums/Pkg-b322a8ff786472d680a972e57575b4586fefe018.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -77e2f3729e58b74cfdd1fc2d991fc6552e7f7ceb7b9fdc837e5885932e2f15d6ab3c76b6ab90b1a6c66050dab7279abc0b976ee52b840b757dd13b1a955f5c5c From 0429a63482c0387ea7ff5ed43931be1d93595b1a Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Thu, 29 Jan 2026 12:14:06 +0100 Subject: [PATCH 35/39] compiler/ssair: preserve negative NewSSAValue (#60688) During incremental compaction a forwarded NewSSAValue with a negative id was treated like an SSAValue, producing SSAValue(-n) and tripping `renumber_ssa2` bounds checks. Only convert NewSSAValue to SSAValue when the id is positive, so new_new_nodes references remain valid. Fixes #57827. --- Compiler/src/ssair/ir.jl | 5 ++++- Compiler/test/irpasses.jl | 16 ++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/Compiler/src/ssair/ir.jl b/Compiler/src/ssair/ir.jl index d7f0dbedcbec5..dc75738267eab 100644 --- a/Compiler/src/ssair/ir.jl +++ b/Compiler/src/ssair/ir.jl @@ -1675,7 +1675,10 @@ function process_node!(compact::IncrementalCompact, result_idx::Int, inst::Instr stmt = ssa_rename[stmt.id] end elseif isa(stmt, NewSSAValue) - stmt = SSAValue(stmt.id) + if stmt.id > 0 + # Negative ids reference new_new_nodes and must remain NewSSAValue. + stmt = SSAValue(stmt.id) + end else # Constant assign, replace uses of this ssa value with its result end diff --git a/Compiler/test/irpasses.jl b/Compiler/test/irpasses.jl index 77a66ec6435fa..6e087f048beae 100644 --- a/Compiler/test/irpasses.jl +++ b/Compiler/test/irpasses.jl @@ -2059,3 +2059,19 @@ let src = code_typed1((Vector{Any},)) do xs end @test count(iscall((src, Core.svec)), src.code) == 1 end + +# Negative NewSSAValue ids must be preserved during compaction +function f_57827(op, init, x) + v = op(init, x) + i = 0 + while i < 1 + v = op(v, x) + i += 1 + end + return v +end +let rf = (acc, x) -> ifelse(x > acc[1], (x,), (acc[1],)) + @test f_57827(rf, (0.0,), 1) === (1,) + ir = first(only(Base.code_ircode(f_57827, (typeof(rf), Tuple{Float64}, Int64); optimize_until="CC: SROA"))) + @test ir isa Compiler.IRCode +end From eaba5ed2138bc6b14440b044e8728c4ef2418121 Mon Sep 17 00:00:00 2001 From: Oscar Smith Date: Thu, 29 Jan 2026 06:10:28 -0500 Subject: [PATCH 36/39] Fix typo in partially_inline! (#60854) Found by claude-code debugging a random zygote issue: https://github.com/SciML/NeuralPDE.jl/pull/1020#issuecomment-3799139274. `spvals` just doesn't exist within this function. This issue has apparently existed since Julia 1.6 --- base/meta.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/meta.jl b/base/meta.jl index 4807b910c494a..79497f6e6d068 100644 --- a/base/meta.jl +++ b/base/meta.jl @@ -409,7 +409,7 @@ function _partially_inline!(@nospecialize(x), slot_replacements::Vector{Any}, end return x elseif head === :cfunction - @assert !isa(type_signature, UnionAll) || !isempty(spvals) + @assert !isa(type_signature, UnionAll) || !isempty(static_param_values) if !isa(x.args[2], QuoteNode) # very common no-op x.args[2] = _partially_inline!(x.args[2], slot_replacements, type_signature, static_param_values, slot_offset, From be1545c8c1b5205ca37c85be2f638bf342db5b60 Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Thu, 29 Jan 2026 12:23:53 +0100 Subject: [PATCH 37/39] ssair: fix phi edge cleanup for current block (#60697) --- Compiler/src/ssair/ir.jl | 26 +++++++++++++++++++------- Compiler/test/ssair.jl | 31 +++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 7 deletions(-) diff --git a/Compiler/src/ssair/ir.jl b/Compiler/src/ssair/ir.jl index dc75738267eab..431ee63f57788 100644 --- a/Compiler/src/ssair/ir.jl +++ b/Compiler/src/ssair/ir.jl @@ -1373,8 +1373,19 @@ function kill_edge!(ir::IRCode, from::Int, to::Int, callback=nothing) kill_edge!(ir.cfg.blocks, from, to, callback) end -# N.B.: from and to are non-renamed indices -function kill_edge!(compact::IncrementalCompact, active_bb::Int, from::Int, to::Int) +@inline function compacted_stmt_range(compact::IncrementalCompact, bb::BasicBlock, active_bb::Int, to::Int) + to == active_bb && return StmtRange(first(bb.stmts), compact.result_idx - 1) + return bb.stmts +end + +""" + kill_edge_terminator!(compact::IncrementalCompact, active_bb::Int, from::Int, to::Int) + +Kill a CFG edge while compacting a terminator in `active_bb`. Assumes all PhiNode +block statements in `to` have already been processed, so the active BB may only +scan the compacted prefix when `to == active_bb`. `from` and `to` are non-renamed indices. +""" +function kill_edge_terminator!(compact::IncrementalCompact, active_bb::Int, from::Int, to::Int) # Note: We recursively kill as many edges as are obviously dead. (; bb_rename_pred, bb_rename_succ, result_bbs, domtree) = compact.cfg_transform preds = result_bbs[bb_rename_succ[to]].preds @@ -1390,7 +1401,7 @@ function kill_edge!(compact::IncrementalCompact, active_bb::Int, from::Int, to:: for succ in copy(to_succs) new_succ = findfirst(x::Int->x==succ, bb_rename_pred) new_succ === nothing && continue - kill_edge!(compact, active_bb, to, new_succ) + kill_edge_terminator!(compact, active_bb, to, new_succ) end empty!(preds) empty!(to_succs) @@ -1412,8 +1423,9 @@ function kill_edge!(compact::IncrementalCompact, active_bb::Int, from::Int, to:: # Remove this edge from all phi nodes in `to` block # NOTE: It is possible for `to` to contain only `nothing` statements, # so we must be careful to stop at its last statement - if to < active_bb - stmts = result_bbs[bb_rename_succ[to]].stmts + if to <= active_bb + bb = result_bbs[bb_rename_succ[to]] + stmts = compacted_stmt_range(compact, bb, active_bb, to) idx = first(stmts) while idx <= last(stmts) stmt = compact.result[idx][:stmt] @@ -1493,14 +1505,14 @@ function process_node!(compact::IncrementalCompact, result_idx::Int, inst::Instr if cond ssa_rename[idx] = nothing result[result_idx][:stmt] = nothing - kill_edge!(compact, active_bb, active_bb, stmt.dest) + kill_edge_terminator!(compact, active_bb, active_bb, stmt.dest) # Don't increment result_idx => Drop this statement else label = bb_rename_succ[stmt.dest] @assert label > 0 ssa_rename[idx] = SSAValue(result_idx) result[result_idx][:stmt] = GotoNode(label) - kill_edge!(compact, active_bb, active_bb, active_bb+1) + kill_edge_terminator!(compact, active_bb, active_bb, active_bb+1) result_idx += 1 end else diff --git a/Compiler/test/ssair.jl b/Compiler/test/ssair.jl index f7ceda07601e2..c9228b30d9e17 100644 --- a/Compiler/test/ssair.jl +++ b/Compiler/test/ssair.jl @@ -108,6 +108,21 @@ let cfg = CFG(BasicBlock[ @test length(compact.cfg_transform.result_bbs) == 4 && 0 in compact.cfg_transform.result_bbs[3].preds end +# Test that removing a self-edge during compaction only scans compacted phi statements. +let code = Any[ + # Block 1 + Compiler.GotoNode(2), + # Block 2 + Core.PhiNode(Int32[1, 3], Any[1, 2]), + Compiler.GotoIfNot(true, 2), + # Block 3 + Compiler.ReturnNode(0), + ] + ir = make_ircode(code) + ir = Compiler.compact!(ir, true) + @test Compiler.verify_ir(ir) === nothing +end + # Issue #32579 - Optimizer bug involving type constraints function f32579(x::Int, b::Bool) if b @@ -850,3 +865,19 @@ let ir = Compiler.inflate_ir!(ci, mi) @test ir isa Compiler.IRCode end + +# Tests that CFG edge cleanup during compaction doesn't corrupt iteration codegen. +Trips_60660 = let + Ts = (Float64, Float32) + [(Ta, Tb, Tc) for Ta in Ts for Tb in Ts for Tc in Ts] +end +@test Trips_60660 == [ + (Float64, Float64, Float64), + (Float64, Float64, Float32), + (Float64, Float32, Float64), + (Float64, Float32, Float32), + (Float32, Float64, Float64), + (Float32, Float64, Float32), + (Float32, Float32, Float64), + (Float32, Float32, Float32), +] From f5754389fdda854ad1bc2a7b6a2c6f4232d58bd1 Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Thu, 29 Jan 2026 20:44:31 +0900 Subject: [PATCH 38/39] inference: fix union split handling for custom lattices (#60857) `MustAlias` has `has_extended_unionsplit(::MustAliasLattice) === true` so it is treated as a target for union split, but `uniontypes` does not handle `MustAlias` specially, causing union split to fail for `MustAlias`. This issue is addressed by always applying `uniontypes` to the result of `widenconst(x)`. External custom lattice implementations could previously provide their own `uniontypes`, but it would be cleaner interface design if they just overload `widenconst` for their custom lattice elements so that it returns `Union`-type. --- Compiler/src/typeutils.jl | 5 +++-- Compiler/test/inference.jl | 9 +++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/Compiler/src/typeutils.jl b/Compiler/src/typeutils.jl index 50b3dc6b0c6f5..5abae32cdb7d6 100644 --- a/Compiler/src/typeutils.jl +++ b/Compiler/src/typeutils.jl @@ -277,8 +277,9 @@ function _switchtupleunion(𝕃::AbstractLattice, t::Vector{Any}, i::Int, tunion _switchtupleunion(𝕃, t, i - 1, tunion, origt) end t[i] = origti - elseif has_extended_unionsplit(𝕃) && !isa(ti, Const) && !isvarargtype(ti) && isa(widenconst(ti), Union) - for ty in uniontypes(ti) + elseif (has_extended_unionsplit(𝕃) && !isa(ti, Const) && !isvarargtype(ti) && + (wty = widenconst(ti); isa(wty, Union))) + for ty in uniontypes(wty) t[i] = ty _switchtupleunion(𝕃, t, i - 1, tunion, origt) end diff --git a/Compiler/test/inference.jl b/Compiler/test/inference.jl index 6b1b964805149..a4bb3773366a3 100644 --- a/Compiler/test/inference.jl +++ b/Compiler/test/inference.jl @@ -2591,6 +2591,15 @@ end == Integer Val(isdefined(xxx.value, :x)) end == Val{true} +# Test union splitting for MustAlias +struct GetSomethingA; x::Union{Nothing,Int}; end +struct GetSomethingB; x::Int; end +getsomethingx(a::GetSomethingA) = something(a.x, 0) +getsomethingx(b::GetSomethingB) = b.x +@test Base.infer_return_type((Union{GetSomethingA,GetSomethingB},); interp=MustAliasInterpreter()) do x + getsomethingx(x) +end == Int + @testset "issue #56913: `BoundsError` in type inference" begin R = UnitRange{Int} @test Type{AbstractVector} == Base.infer_return_type(Base.promote_typeof, Tuple{R, R, Vector{Any}, Vararg{R}}) From 4938a3e3389335a56feb5e5fdf0763d8b6b65b3a Mon Sep 17 00:00:00 2001 From: rokke <66498307+rokke-git@users.noreply.github.com> Date: Wed, 28 Jan 2026 00:16:20 -0500 Subject: [PATCH 39/39] Merge pull request #60834 from rokke-git/patch-5 Test suite: switch to `httpbingo.julialang`, fall back to `httpbin.julialang` on failure (cherry picked from commit 5ab8db39c014dfcf1b53314932dddfea35ecb2c7) (cherry picked from commit e4cfa466a924b03fc102161c448251b2bfbe0d38) --- test/download_exec.jl | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/test/download_exec.jl b/test/download_exec.jl index 777fb6773c463..93af540dd70a1 100644 --- a/test/download_exec.jl +++ b/test/download_exec.jl @@ -5,22 +5,31 @@ module TestDownload using Test mktempdir() do temp_dir + url = try + download("https://httpbingo.julialang.org") + "https://httpbingo.julialang.org" + catch ex + bt = catch_backtrace() + @info "Looks like there is a problem with a JuliaLang mirror of httpbingo" + @info "Trying httpbin" exception=(ex,bt) + "https://httpbin.julialang.org/" + end # Download a file file = joinpath(temp_dir, "ip") - @test download("https://httpbin.julialang.org/ip", file) == file + @test download("$url/ip", file) == file @test isfile(file) @test !isempty(read(file)) ip = read(file, String) # Download an empty file empty_file = joinpath(temp_dir, "empty") - @test download("https://httpbin.julialang.org/status/200", empty_file) == empty_file + @test download("$url/status/200", empty_file) == empty_file @test isfile(empty_file) @test isempty(read(empty_file)) # Make sure that failed downloads do not leave files around missing_file = joinpath(temp_dir, "missing") - @test_throws Exception download("https://httpbin.julialang.org/status/404", missing_file) + @test_throws Exception download("$url/status/404", missing_file) @test !isfile(missing_file) # Use a TEST-NET (192.0.2.0/24) address which shouldn't be bound