diff --git a/.github/labeler.yml b/.github/labeler.yml new file mode 100644 index 0000000000000..9359bd0ca70a6 --- /dev/null +++ b/.github/labeler.yml @@ -0,0 +1,3 @@ +# See https://github.com/actions/labeler +port-to-master: '**' +port-to-v1.10: '**' diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000000000..7ed825781c53b --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,14 @@ + +## PR Description + +_What does this PR do?_ + +## Checklist + +Requirements for merging: +- [ ] I have opened an issue or PR upstream on JuliaLang/julia: +- [ ] I have removed the `port-to-*` labels that don't apply. +- [ ] I have opened a PR on raicode to test these changes: diff --git a/.github/workflows/Typos.yml b/.github/workflows/Typos.yml index 6c9eeacc21800..25cf9fea7cd0b 100644 --- a/.github/workflows/Typos.yml +++ b/.github/workflows/Typos.yml @@ -11,7 +11,7 @@ jobs: timeout-minutes: 5 steps: - name: Checkout the JuliaLang/julia repository - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: Check spelling with typos diff --git a/.github/workflows/Whitespace.yml b/.github/workflows/Whitespace.yml index 37c9dbfd39a3c..9b800da77dc29 100644 --- a/.github/workflows/Whitespace.yml +++ b/.github/workflows/Whitespace.yml @@ -15,10 +15,10 @@ jobs: timeout-minutes: 2 steps: - name: Checkout the JuliaLang/julia repository - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - - uses: julia-actions/setup-julia@9b79636afcfb07ab02c256cede01fe2db6ba808c # v2.6.0 + - uses: julia-actions/setup-julia@5c9647d97b78a5debe5164e9eec09d653d29bd71 # v2.6.1 with: version: '1' - name: Check whitespace diff --git a/.github/workflows/cffconvert.yml b/.github/workflows/cffconvert.yml index 4c9debb246f3f..45f19078a8af8 100644 --- a/.github/workflows/cffconvert.yml +++ b/.github/workflows/cffconvert.yml @@ -23,7 +23,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Check out a copy of the repository - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml new file mode 100644 index 0000000000000..2141a906e96cd --- /dev/null +++ b/.github/workflows/labeler.yml @@ -0,0 +1,17 @@ +# See https://github.com/actions/labeler +name: "Pull Request Labeler" +on: + pull_request_target: + types: + - opened + +jobs: + triage: + permissions: + contents: read + pull-requests: write + runs-on: ubuntu-latest + steps: + - uses: actions/labeler@v4 + with: + dot: true diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml new file mode 100644 index 0000000000000..3df2093491753 --- /dev/null +++ b/.github/workflows/stale.yml @@ -0,0 +1,16 @@ +name: "Close stale PRs" +on: + schedule: + - cron: "0 0 * * *" # every night at midnight + +jobs: + stale: + runs-on: ubuntu-latest + steps: + - uses: actions/stale@v8 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + stale-pr-message: 'This PR is stale because it has been open 30 days with no activity. Comment or remove stale label, or this PR will be closed in 5 days.' + days-before-stale: 30 + days-before-close: 5 + stale-pr-label: 'stale' diff --git a/.github/workflows/update-upstream-branches.yml b/.github/workflows/update-upstream-branches.yml new file mode 100644 index 0000000000000..247000bbd42cd --- /dev/null +++ b/.github/workflows/update-upstream-branches.yml @@ -0,0 +1,28 @@ +name: "Update upstream branches" +on: + schedule: + - cron: "0 0 * * *" # every night at midnight + workflow_dispatch: + +jobs: + PullUpstream: + runs-on: ubuntu-latest + strategy: + fail-fast: false # run all jobs in the matrix even if one fails + matrix: + branch: + - "master" + - "backports-release-1.10" + steps: + - name: Checkout RAI/julia + uses: actions/checkout@v3 + with: + ref: ${{ matrix.branch }} + - name: Update ${{ matrix.branch }} + run: | + git config --global user.email "julia-engineering@relational.ai" + git config --global user.name "RAI CI (GitHub Action Automation)" + + git remote add upstream https://github.com/JuliaLang/julia + git pull upstream ${{ matrix.branch }} + git push origin ${{ matrix.branch }} diff --git a/Compiler/src/abstractinterpretation.jl b/Compiler/src/abstractinterpretation.jl index b455f078d83eb..f27a68da44b90 100644 --- a/Compiler/src/abstractinterpretation.jl +++ b/Compiler/src/abstractinterpretation.jl @@ -2247,7 +2247,7 @@ function abstract_invoke(interp::AbstractInterpreter, arginfo::ArgInfo, si::Stmt return Future(CallMeta(Bottom, ErrorException, EFFECTS_THROWS, NoCallInfo())) end # TODO: When we add curing, we may want to assume this is nothrow - if (method_or_ci.owner === Nothing && method_ir_ci.def.def isa Method) + if (method_or_ci.owner === Nothing && method_or_ci.def.def isa Method) exct = Union{exct, ErrorException} end update_valid_age!(sv, callee_valid_range) diff --git a/Compiler/src/ssair/ir.jl b/Compiler/src/ssair/ir.jl index d7f0dbedcbec5..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 @@ -1675,7 +1687,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/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/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}}) 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 diff --git a/Compiler/test/ssair.jl b/Compiler/test/ssair.jl index 80faac97d83a4..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 @@ -838,3 +853,31 @@ 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 + +# 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), +] diff --git a/Make.inc b/Make.inc index c2ccf6757e3c9..a145980e0264c 100644 --- a/Make.inc +++ b/Make.inc @@ -381,7 +381,7 @@ $(foreach D,build_libdir build_private_libdir,$(eval $(call cache_rel_path,$(D), # Save a special one: reverse_private_libdir_rel: usually just `../`, but good to be general: reverse_private_libdir_rel_eval = $(call rel_path,$(private_libdir),$(libdir)) reverse_private_libdir_rel = $(call hit_cache,reverse_private_libdir_rel_eval) -reverse_private_libexecdir_rel_eval = $(call rel_path,$(private_libexecdir),$(libdir)) +reverse_private_libexecdir_rel_eval = $(call rel_path,$(private_libexecdir),$(private_libdir)) reverse_private_libexecdir_rel = $(call hit_cache,reverse_private_libexecdir_rel_eval) reverse_build_private_libexecdir_rel_eval = $(call rel_path,$(build_private_libexecdir),$(build_libdir)) reverse_build_private_libexecdir_rel = $(call hit_cache,reverse_build_private_libexecdir_rel_eval) diff --git a/Makefile b/Makefile index bae449b255a10..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 @@ -325,8 +325,7 @@ else ifeq ($(JULIA_BUILD_MODE),debug) -$(INSTALL_M) $(build_libdir)/libjulia-debug.dll.a $(DESTDIR)$(libdir)/ -$(INSTALL_M) $(build_libdir)/libjulia-internal-debug.dll.a $(DESTDIR)$(libdir)/ endif - -$(INSTALL_M) $(wildcard $(build_private_libdir)/*.a) $(DESTDIR)$(private_libdir)/ - -rm -f $(DESTDIR)$(private_libdir)/sys-o.a + -$(INSTALL_M) $(filter-out %-bc.a %-o.a,$(wildcard $(build_private_libdir)/lib*.a)) $(DESTDIR)$(private_libdir)/ -$(INSTALL_M) $(build_bindir)/libopenlibm.dll.a $(DESTDIR)$(libdir)/ -$(INSTALL_M) $(build_libdir)/libssp.dll.a $(DESTDIR)$(libdir)/ @@ -485,6 +484,37 @@ else ifeq ($(JULIA_BUILD_MODE),debug) $(PATCHELF) $(PATCHELF_SET_RPATH_ARG) '$$ORIGIN:$$ORIGIN/$(reverse_private_libdir_rel)' $(DESTDIR)$(private_libdir)/libjulia-internal-debug.$(SHLIB_EXT) $(PATCHELF) $(PATCHELF_SET_RPATH_ARG) '$$ORIGIN:$$ORIGIN/$(reverse_private_libdir_rel)' $(DESTDIR)$(private_libdir)/libjulia-codegen-debug.$(SHLIB_EXT) endif +endif + +ifeq ($(OS), Darwin) +ifneq ($(DARWIN_FRAMEWORK),1) + for j in $(JL_PRIVATE_TOOLS) ; do \ + [ -L $(DESTDIR)$(private_libexecdir)/$$j ] && continue; \ + install_name_tool -rpath @loader_path/$(build_libdir_rel) @executable_path/$(reverse_private_libexecdir_rel) $(DESTDIR)$(private_libexecdir)/$$j || exit 1; \ + done +endif +else ifneq (,$(findstring $(OS),Linux FreeBSD)) + for j in $(JL_PRIVATE_TOOLS) ; do \ + [ -L $(DESTDIR)$(private_libexecdir)/$$j ] && continue; \ + $(PATCHELF) $(PATCHELF_SET_RPATH_ARG) '$$ORIGIN/$(reverse_private_libexecdir_rel)' $(DESTDIR)$(private_libexecdir)/$$j || exit 1; \ + done +endif + +ifneq ($(reverse_private_libexecdir_rel),$(reverse_build_private_libexecdir_rel)) +ifeq ($(OS), Darwin) +ifneq ($(DARWIN_FRAMEWORK),1) + for j in $(JL_PRIVATE_EXES) ; do \ + [ $$j = 7z ] && continue; \ + [ -L $(DESTDIR)$(private_libexecdir)/$$j ] && continue; \ + install_name_tool -rpath @executable_path/$(reverse_build_private_libexecdir_rel) @executable_path/$(reverse_private_libexecdir_rel) $(DESTDIR)$(private_libexecdir)/$$j || exit 1; \ + done +endif +else ifneq (,$(findstring $(OS),Linux FreeBSD)) + for j in $(JL_PRIVATE_EXES) ; do \ + [ -L $(DESTDIR)$(private_libexecdir)/$$j ] && continue; \ + $(PATCHELF) $(PATCHELF_SET_RPATH_ARG) '$$ORIGIN/$(reverse_private_libexecdir_rel)' $(DESTDIR)$(private_libexecdir)/$$j || exit 1; \ + done +endif endif # Fix rpaths for dependencies. This should be fixed in BinaryBuilder later. diff --git a/NEWS.md b/NEWS.md index f5f68e508bdd9..17dd8ced3d56c 100644 --- a/NEWS.md +++ b/NEWS.md @@ -8,7 +8,7 @@ New language features entry points. Entry points can be marked using `Base.Experimental.entrypoint` ([#55047]). Not all code is expected to work with this option, and since it is experimental you may encounter problems. * Redefinition of constants is now well defined and follows world age semantics ([#57253]). Additional redefinitions - (e.g. of types) are now allowed. See [the new manual chapter on world age](https://docs.julialang.org/en/v1.13-dev/manual/worldage/). + (e.g. of types) are now allowed. See [the new manual chapter on world age](https://docs.julialang.org/en/v1/manual/worldage/). * A new keyword argument `usings::Bool` has been added to `names`, returning all names visible via `using` ([#54609]). * The `@atomic` macro family now supports reference assignment syntax, e.g. `@atomic :monotonic v[3] += 4`, diff --git a/VERSION b/VERSION index 6b89d58f861a7..3c27091f99178 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.12.2 +1.12.5+RAI 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)" "$@" diff --git a/base/array.jl b/base/array.jl index 665934b8a3a11..9674bc170ad6e 100644 --- a/base/array.jl +++ b/base/array.jl @@ -641,7 +641,7 @@ julia> collect(Float64, 1:2:5) collect(::Type{T}, itr) where {T} = _collect(T, itr, IteratorSize(itr)) _collect(::Type{T}, itr, isz::Union{HasLength,HasShape}) where {T} = - copyto!(_array_for(T, isz, _similar_shape(itr, isz)), itr) + copyto!(_array_for_inner(T, isz, _similar_shape(itr, isz)), itr) function _collect(::Type{T}, itr, isz::SizeUnknown) where T a = Vector{T}() for x in itr @@ -665,12 +665,12 @@ _similar_for(c::AbstractArray, ::Type{T}, itr, ::HasShape, axs) where {T} = similar(c, T, axs) # make a collection appropriate for collecting `itr::Generator` -_array_for(::Type{T}, ::SizeUnknown, ::Nothing) where {T} = Vector{T}(undef, 0) -_array_for(::Type{T}, ::HasLength, len::Integer) where {T} = Vector{T}(undef, Int(len)) -_array_for(::Type{T}, ::HasShape{N}, axs) where {T,N} = similar(Array{T,N}, axs) +_array_for_inner(::Type{T}, ::SizeUnknown, ::Nothing) where {T} = Vector{T}(undef, 0) +_array_for_inner(::Type{T}, ::HasLength, len::Integer) where {T} = Vector{T}(undef, Int(len)) +_array_for_inner(::Type{T}, ::HasShape{N}, axs) where {T,N} = similar(Array{T,N}, axs) # used by syntax lowering for simple typed comprehensions -_array_for(::Type{T}, itr, isz) where {T} = _array_for(T, isz, _similar_shape(itr, isz)) +_array_for(::Type{T}, itr, isz) where {T} = _array_for_inner(T, isz, _similar_shape(itr, isz)) """ @@ -789,10 +789,10 @@ function collect(itr::Generator) shp = _similar_shape(itr, isz) y = iterate(itr) if y === nothing - return _array_for(et, isz, shp) + return _array_for_inner(et, isz, shp) end v1, st = y - dest = _array_for(typeof(v1), isz, shp) + dest = _array_for_inner(typeof(v1), isz, shp) # The typeassert gives inference a helping hand on the element type and dimensionality # (work-around for #28382) et′ = et <: Type ? Type : et diff --git a/base/bitarray.jl b/base/bitarray.jl index 93fa48c56e379..2b73b1ea8a710 100644 --- a/base/bitarray.jl +++ b/base/bitarray.jl @@ -151,16 +151,14 @@ function copy_chunks!(dest::Vector{UInt64}, pos_d::Int, src::Vector{UInt64}, pos delta_ks = ks1 - ks0 u = _msk64 + msk_d0 = ~(u << ld0) + msk_d1 = (u << (ld1+1)) if delta_kd == 0 - msk_d0 = ~(u << ld0) | (u << (ld1+1)) - else - msk_d0 = ~(u << ld0) - msk_d1 = (u << (ld1+1)) + msk_d0 |= msk_d1 end + msk_s0 = (u << ls0) if delta_ks == 0 - msk_s0 = (u << ls0) & ~(u << (ls1+1)) - else - msk_s0 = (u << ls0) + msk_s0 &= ~(u << (ls1+1)) end chunk_s0 = glue_src_bitchunks(src, ks0, ks1, msk_s0, ls0) @@ -211,16 +209,14 @@ function copy_chunks_rtol!(chunks::Vector{UInt64}, pos_d::Int, pos_s::Int, numbi delta_kd = kd1 - kd0 delta_ks = ks1 - ks0 + msk_d0 = ~(u << ld0) + msk_d1 = (u << (ld1+1)) if delta_kd == 0 - msk_d0 = ~(u << ld0) | (u << (ld1+1)) - else - msk_d0 = ~(u << ld0) - msk_d1 = (u << (ld1+1)) + msk_d0 |= msk_d1 end + msk_s0 = (u << ls0) if delta_ks == 0 - msk_s0 = (u << ls0) & ~(u << (ls1+1)) - else - msk_s0 = (u << ls0) + msk_s0 &= ~(u << (ls1+1)) end chunk_s0 = glue_src_bitchunks(chunks, ks0, ks1, msk_s0, ls0) & ~(u << s) @@ -246,11 +242,10 @@ function fill_chunks!(Bc::Array{UInt64}, x::Bool, pos::Int, numbits::Int) k1, l1 = get_chunks_id(pos+numbits-1) u = _msk64 + msk0 = (u << l0) + msk1 = ~(u << (l1+1)) if k1 == k0 - msk0 = (u << l0) & ~(u << (l1+1)) - else - msk0 = (u << l0) - msk1 = ~(u << (l1+1)) + msk0 &= msk1 end @inbounds if x Bc[k0] |= msk0 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) diff --git a/base/file.jl b/base/file.jl index ce9bc76256fd3..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 @@ -1267,22 +1267,19 @@ function rename(oldpath::AbstractString, newpath::AbstractString) end function sendfile(src::AbstractString, dst::AbstractString) - src_open = false - dst_open = false - local src_file, dst_file + src_file = nothing + dst_file = nothing try src_file = open(src, JL_O_RDONLY) - src_open = true dst_file = open(dst, JL_O_CREAT | JL_O_TRUNC | JL_O_WRONLY, filemode(src_file)) - dst_open = true bytes = filesize(stat(src_file)) sendfile(dst_file, src_file, Int64(0), Int(bytes)) finally - if src_open && isopen(src_file) + if src_file !== nothing && isopen(src_file) close(src_file) end - if dst_open && isopen(dst_file) + if dst_file !== nothing && isopen(dst_file) close(dst_file) end end diff --git a/base/loading.jl b/base/loading.jl index 40db7df679b60..a804cc5f54706 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -4211,6 +4211,20 @@ function expand_compiler_path(tup) end compiler_chi(tup::Tuple) = CacheHeaderIncludes(expand_compiler_path(tup)) +""" + isprecompilable(f, argtypes::Tuple{Vararg{Any}}) + +Check, as far as is possible without actually compiling, if the given +function `f` can be compiled for the argument tuple (of types) `argtypes`. +""" +function isprecompilable(@nospecialize(f), @nospecialize(argtypes::Tuple)) + isprecompilable(Tuple{Core.Typeof(f), argtypes...}) +end + +function isprecompilable(@nospecialize(argt::Type)) + ccall(:jl_is_compilable, Int32, (Any,), argt) != 0 +end + """ precompile(f, argtypes::Tuple{Vararg{Any}}) diff --git a/base/lock.jl b/base/lock.jl index e74da826900a6..1feea348e5ac4 100644 --- a/base/lock.jl +++ b/base/lock.jl @@ -366,7 +366,7 @@ This is similar to using [`lock`](@ref) with a `do` block, but avoids creating a and thus can improve the performance. !!! compat - `@lock` was added in Julia 1.3, and exported in Julia 1.10. + `@lock` was added in Julia 1.3, and exported in Julia 1.7. """ macro lock(l, expr) quote diff --git a/base/logging/logging.jl b/base/logging/logging.jl index c44e74ffa627b..bb24a0e47b05b 100644 --- a/base/logging/logging.jl +++ b/base/logging/logging.jl @@ -129,6 +129,8 @@ end LogLevel(level::LogLevel) = level isless(a::LogLevel, b::LogLevel) = isless(a.level, b.level) +isless(a::LogLevel, b::Integer) = isless(a.level, b) +isless(a::Integer, b::LogLevel) = isless(a, b.level) +(level::LogLevel, inc::Integer) = LogLevel(level.level+inc) -(level::LogLevel, inc::Integer) = LogLevel(level.level-inc) convert(::Type{LogLevel}, level::Integer) = LogLevel(level) 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, diff --git a/base/options.jl b/base/options.jl index 1692c765443f3..b5730994492aa 100644 --- a/base/options.jl +++ b/base/options.jl @@ -64,6 +64,7 @@ struct JLOptions trim::Int8 task_metrics::Int8 timeout_for_safepoint_straggler_s::Int16 + safe_crash_log_file::Ptr{UInt8} end # This runs early in the sysimage != is not defined yet diff --git a/base/partr.jl b/base/partr.jl index d488330f0c87e..671c88fd6e786 100644 --- a/base/partr.jl +++ b/base/partr.jl @@ -97,7 +97,7 @@ function multiq_sift_down(heap::taskheap, idx::Int32) child = Int(child) child > length(heap.tasks) && break if isassigned(heap.tasks, child) && - heap.tasks[child].priority < heap.tasks[idx].priority + heap.tasks[child].priority <= heap.tasks[idx].priority t = heap.tasks[idx] heap.tasks[idx] = heap.tasks[child] heap.tasks[child] = t 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 diff --git a/base/process.jl b/base/process.jl index fbc4acfd83e80..1a2324cb5d86e 100644 --- a/base/process.jl +++ b/base/process.jl @@ -131,15 +131,14 @@ end if err == 0 pp = Process(cmd, handle, syncd) associate_julia_struct(handle, pp) + iolock_end() + return pp else ccall(:jl_forceclose_uv, Cvoid, (Ptr{Cvoid},), handle) # will call free on handle eventually + iolock_end() + throw(_UVError("could not spawn " * repr(cmd), err)) end - iolock_end() end - if err != 0 - throw(_UVError("could not spawn " * repr(cmd), err)) - end - return pp end _spawn(cmds::AbstractCmd) = _spawn(cmds, SpawnIOs()) 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 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/base/timing.jl b/base/timing.jl index e937d396a52a2..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 """ @@ -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/base/toml_parser.jl b/base/toml_parser.jl index 378322e8cba95..46208eeff4195 100644 --- a/base/toml_parser.jl +++ b/base/toml_parser.jl @@ -716,8 +716,8 @@ function parse_array(l::Parser{Dates})::Err{Vector} where Dates copyto_typed!(new, array) elseif T === Union{} new = Any[] - elseif (T === TOMLDict) || (T == BigInt) || (T === UInt128) || (T === Int128) || (T <: Vector) || - (T === Dates.Date) || (T === Dates.Time) || (T === Dates.DateTime) + elseif (T === TOMLDict) || (T === BigInt) || (T === UInt128) || (T === Int128) || (T <: Vector) || + (Dates !== nothing && ((T === Dates.Date) || (T === Dates.Time) || (T === Dates.DateTime))) # do nothing, leave as Vector{Any} new = array else @assert false end diff --git a/contrib/mac/app/Makefile b/contrib/mac/app/Makefile index 70436a857c265..81b7e47cdf2cf 100644 --- a/contrib/mac/app/Makefile +++ b/contrib/mac/app/Makefile @@ -47,8 +47,8 @@ dmg/$(APP_NAME): startup.applescript julia.icns plutil -insert CFBundleVersion -string "$(JULIA_VERSION_OPT_COMMIT)" $@/Contents/Info.plist plutil -insert NSHumanReadableCopyright -string "$(APP_COPYRIGHT)" $@/Contents/Info.plist -mkdir -p $@/Contents/Resources/julia - $(MAKE) -C $(JULIAHOME) binary-dist - $(TAR) -xzf $(JULIAHOME)/$(JULIA_BINARYDIST_FILENAME).tar.gz -C $@/Contents/Resources/julia --strip-components 1 + make -C $(JULIAHOME) binary-dist + tar zxf $(JULIAHOME)/$(JULIA_BINARYDIST_FILENAME).tar.gz -C $@/Contents/Resources/julia --strip-components 1 find $@/Contents/Resources/julia -type f -exec chmod -w {} \; # Even though the tarball may already be signed, we re-sign here to make it easier to add # unsigned executables (like the app launcher) and whatnot, without needing to maintain lists 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 diff --git a/deps/checksums/LinearAlgebra-7249e662d66b53da6c9335ea748eac2750c2924f.tar.gz/md5 b/deps/checksums/LinearAlgebra-7249e662d66b53da6c9335ea748eac2750c2924f.tar.gz/md5 new file mode 100644 index 0000000000000..cbd119f9a07e5 --- /dev/null +++ b/deps/checksums/LinearAlgebra-7249e662d66b53da6c9335ea748eac2750c2924f.tar.gz/md5 @@ -0,0 +1 @@ +c8fea09ab0a67dcd9bacc413a008056b diff --git a/deps/checksums/LinearAlgebra-7249e662d66b53da6c9335ea748eac2750c2924f.tar.gz/sha512 b/deps/checksums/LinearAlgebra-7249e662d66b53da6c9335ea748eac2750c2924f.tar.gz/sha512 new file mode 100644 index 0000000000000..01c026a488114 --- /dev/null +++ b/deps/checksums/LinearAlgebra-7249e662d66b53da6c9335ea748eac2750c2924f.tar.gz/sha512 @@ -0,0 +1 @@ +f44e5518d45d568a56116fcef058c006adefff6752b7bfecd29afffa7fc797739271bde2d3a6d0813d89973c862341d5ee47347a933146a43108ea716b133672 diff --git a/deps/checksums/LinearAlgebra-997c4b7e7664f30645ef8bd51d8a4203e49c4631.tar.gz/md5 b/deps/checksums/LinearAlgebra-997c4b7e7664f30645ef8bd51d8a4203e49c4631.tar.gz/md5 deleted file mode 100644 index 0981743e0907a..0000000000000 --- a/deps/checksums/LinearAlgebra-997c4b7e7664f30645ef8bd51d8a4203e49c4631.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -1cf83c7ea8935ac0804e07ebe8ce7506 diff --git a/deps/checksums/LinearAlgebra-997c4b7e7664f30645ef8bd51d8a4203e49c4631.tar.gz/sha512 b/deps/checksums/LinearAlgebra-997c4b7e7664f30645ef8bd51d8a4203e49c4631.tar.gz/sha512 deleted file mode 100644 index 971b146eecf64..0000000000000 --- a/deps/checksums/LinearAlgebra-997c4b7e7664f30645ef8bd51d8a4203e49c4631.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -ec3edd7208e25fb1af0b9cb2324abbfb66e556442b75df619ffdf428f1d59f851055117db896fd3514352a32c34c4c42cf50e1386deca4ebd051de98369d5a3e 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/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-53b2b5da91c27515ce129635fe184e8bd9afb09f.tar.gz/md5 b/deps/checksums/Pkg-53b2b5da91c27515ce129635fe184e8bd9afb09f.tar.gz/md5 deleted file mode 100644 index 48b17f7ee32d2..0000000000000 --- a/deps/checksums/Pkg-53b2b5da91c27515ce129635fe184e8bd9afb09f.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -5c898e09839cfa16f80940b08bdebe9f diff --git a/deps/checksums/Pkg-53b2b5da91c27515ce129635fe184e8bd9afb09f.tar.gz/sha512 b/deps/checksums/Pkg-53b2b5da91c27515ce129635fe184e8bd9afb09f.tar.gz/sha512 deleted file mode 100644 index 7236505c4e9f1..0000000000000 --- a/deps/checksums/Pkg-53b2b5da91c27515ce129635fe184e8bd9afb09f.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -5202b09eaf172291260bc69a28944986c80a4362a9ef521b05c8ce95c69c9909439b5b2f3a52326ab820f1eff2e1ba2a8ddaa90080d9e29fd2f6e226856bda3e 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-5d674dc7bd90156cf8ecea4e143b69b5a5b7640d.tar.gz/md5 b/deps/checksums/SparseArrays-5d674dc7bd90156cf8ecea4e143b69b5a5b7640d.tar.gz/md5 deleted file mode 100644 index ab3e4ebfe9f25..0000000000000 --- a/deps/checksums/SparseArrays-5d674dc7bd90156cf8ecea4e143b69b5a5b7640d.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -eadaa92895c8d4d33eb601165ef765d5 diff --git a/deps/checksums/SparseArrays-5d674dc7bd90156cf8ecea4e143b69b5a5b7640d.tar.gz/sha512 b/deps/checksums/SparseArrays-5d674dc7bd90156cf8ecea4e143b69b5a5b7640d.tar.gz/sha512 deleted file mode 100644 index 7a250e67b00d3..0000000000000 --- a/deps/checksums/SparseArrays-5d674dc7bd90156cf8ecea4e143b69b5a5b7640d.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -bb37377b360eca1a32c78b1d11b83c7e918a8ddb9df79388694b6f415dc5d5cf6182df7437869b3970011e5dcda4a3f821b58498bfa6fd7df697fcd51383ca12 diff --git a/deps/checksums/cacert-2025-11-04.pem/md5 b/deps/checksums/cacert-2025-11-04.pem/md5 new file mode 100644 index 0000000000000..641a98aecef02 --- /dev/null +++ b/deps/checksums/cacert-2025-11-04.pem/md5 @@ -0,0 +1 @@ +4ca8e1c3e8fc44c3ecd7a1fb9d3a6d03 diff --git a/deps/checksums/cacert-2025-11-04.pem/sha512 b/deps/checksums/cacert-2025-11-04.pem/sha512 new file mode 100644 index 0000000000000..bbd48b9475d7f --- /dev/null +++ b/deps/checksums/cacert-2025-11-04.pem/sha512 @@ -0,0 +1 @@ +9d9f7ecc829bafc222501d8a66852d96a51f522b04a82963e4166c87b85d6a5e5eedb50ced2ef3026cd7cb06fcb4b7dca59c4157813a067cb7b185e32f2957ec diff --git a/deps/libgit2.mk b/deps/libgit2.mk index dd0d883c2e736..8b17ae6d70424 100644 --- a/deps/libgit2.mk +++ b/deps/libgit2.mk @@ -4,7 +4,6 @@ ifneq ($(USE_BINARYBUILDER_LIBGIT2),1) LIBGIT2_GIT_URL := https://github.com/libgit2/libgit2.git LIBGIT2_TAR_URL = https://api.github.com/repos/libgit2/libgit2/tarball/$1 $(eval $(call git-external,libgit2,LIBGIT2,CMakeLists.txt,,$(SRCCACHE))) -$(SRCCACHE)/$(LIBGIT2_SRC_DIR)/source-extracted: $(MSYS_NONEXISTENT_SYMLINK_TARGET_FIX) ifeq ($(USE_SYSTEM_LIBSSH2), 0) $(BUILDDIR)/$(LIBGIT2_SRC_DIR)/build-configured: | $(build_prefix)/manifest/libssh2 @@ -14,15 +13,7 @@ ifeq ($(USE_SYSTEM_OPENSSL), 0) $(BUILDDIR)/$(LIBGIT2_SRC_DIR)/build-configured: | $(build_prefix)/manifest/openssl endif -ifeq ($(USE_SYSTEM_PCRE), 0) -$(BUILDDIR)/$(LIBGIT2_SRC_DIR)/build-configured: | $(build_prefix)/manifest/pcre -endif - -ifeq ($(USE_SYSTEM_ZLIB), 0) -$(BUILDDIR)/$(LIBGIT2_SRC_DIR)/build-configured: | $(build_prefix)/manifest/zlib -endif - -LIBGIT2_OPTS := $(CMAKE_COMMON) -DCMAKE_BUILD_TYPE=Release -DUSE_THREADS=ON -DUSE_BUNDLED_ZLIB=OFF -DUSE_SSH=ON -DREGEX_BACKEND=pcre2 -DBUILD_CLI=OFF -DBUILD_TESTS=OFF +LIBGIT2_OPTS := $(CMAKE_COMMON) -DCMAKE_BUILD_TYPE=Release -DUSE_THREADS=ON -DUSE_BUNDLED_ZLIB=ON -DUSE_SSH=ON -DBUILD_CLI=OFF ifeq ($(OS),WINNT) LIBGIT2_OPTS += -DWIN32=ON -DMINGW=ON ifeq ($(USE_SYSTEM_LIBSSH2), 0) diff --git a/deps/libgit2.version b/deps/libgit2.version index ffba640e3b24e..5a7fb591098bb 100644 --- a/deps/libgit2.version +++ b/deps/libgit2.version @@ -11,4 +11,4 @@ LIBGIT2_SHA1=338e6fb681369ff0537719095e22ce9dc602dbf0 # The versions of cacert.pem are identified by the date (YYYY-MM-DD) of their changes. # See https://curl.haxx.se/docs/caextract.html for more details. # Keep in sync with `stdlib/MozillaCACerts_jll/Project.toml`. -MOZILLA_CACERT_VERSION := 2025-05-20 +MOZILLA_CACERT_VERSION := 2025-11-04 diff --git a/deps/libssh2.mk b/deps/libssh2.mk index 05cc12b6e159b..661e26516c828 100644 --- a/deps/libssh2.mk +++ b/deps/libssh2.mk @@ -17,6 +17,9 @@ endif ifeq ($(OS),WINNT) LIBSSH2_OPTS += -DCRYPTO_BACKEND=WinCNG -DENABLE_ZLIB_COMPRESSION=OFF +ifeq ($(BUILD_OS),WINNT) +LIBSSH2_OPTS += -G"MSYS Makefiles" +endif else LIBSSH2_OPTS += -DCRYPTO_BACKEND=OpenSSL -DENABLE_ZLIB_COMPRESSION=OFF LIBSSH2_OPTS += -DOPENSSL_ROOT_DIR=$(build_prefix) @@ -35,7 +38,7 @@ LIBSSH2_SRC_PATH := $(SRCCACHE)/$(LIBSSH2_SRC_DIR) $(BUILDDIR)/$(LIBSSH2_SRC_DIR)/build-configured: $(LIBSSH2_SRC_PATH)/source-extracted mkdir -p $(dir $@) cd $(dir $@) && \ - $(CMAKE) $(CMAKE_GENERATOR_COMMAND) $(dir $<) $(LIBSSH2_OPTS) + $(CMAKE) $(dir $<) $(LIBSSH2_OPTS) echo 1 > $@ $(BUILDDIR)/$(LIBSSH2_SRC_DIR)/build-compiled: $(BUILDDIR)/$(LIBSSH2_SRC_DIR)/build-configured diff --git a/deps/libuv.mk b/deps/libuv.mk index 993aa4fc144da..eacabac55e34f 100644 --- a/deps/libuv.mk +++ b/deps/libuv.mk @@ -4,7 +4,7 @@ LIBUV_GIT_URL:=https://github.com/JuliaLang/libuv.git LIBUV_TAR_URL=https://api.github.com/repos/JuliaLang/libuv/tarball/$1 $(eval $(call git-external,libuv,LIBUV,configure,,$(SRCCACHE))) -UV_CFLAGS := -O2 -DBUILDING_UV_SHARED=1 +UV_CFLAGS := -O2 UV_FLAGS := LDFLAGS="$(LDFLAGS) $(CLDFLAGS) -v" UV_FLAGS += CFLAGS="$(CFLAGS) $(UV_CFLAGS) $(SANITIZE_OPTS)" diff --git a/deps/llvm.mk b/deps/llvm.mk index 8b823b8664bf2..09dd4f187d611 100644 --- a/deps/llvm.mk +++ b/deps/llvm.mk @@ -7,14 +7,6 @@ ifneq ($(USE_BINARYBUILDER_LLVM), 1) LLVM_GIT_URL:=https://github.com/JuliaLang/llvm-project.git LLVM_TAR_URL=https://api.github.com/repos/JuliaLang/llvm-project/tarball/$1 $(eval $(call git-external,llvm,LLVM,CMakeLists.txt,,$(SRCCACHE))) -# LLVM's tarball contains symlinks to non-existent targets. This breaks the -# the default msys strategy `deepcopy` symlink strategy. To workaround this, -# switch to `native` which tries native windows symlinks (possible if the -# machine is in developer mode) - or if not, falls back to cygwin-style -# symlinks. We don't particularly care either way - we just need to symlinks -# to succeed. We could guard this by a uname check, but it's harmless elsewhere, -# so let's not incur the additional overhead. -$(SRCCACHE)/$(LLVM_SRC_DIR)/source-extracted: $(MSYS_NONEXISTENT_SYMLINK_TARGET_FIX) LLVM_BUILDDIR := $(BUILDDIR)/$(LLVM_SRC_DIR) LLVM_BUILDDIR_withtype := $(LLVM_BUILDDIR)/build_$(LLVM_BUILDTYPE) diff --git a/deps/openblas.mk b/deps/openblas.mk index e5a988ba84df2..80881a6a13c4e 100644 --- a/deps/openblas.mk +++ b/deps/openblas.mk @@ -44,7 +44,7 @@ OPENBLAS_CFLAGS := -O2 # Decide whether to build for 32-bit or 64-bit arch ifneq ($(XC_HOST),) -OPENBLAS_BUILD_OPTS += OSNAME=$(OS) CROSS=1 HOSTCC=$(HOSTCC) CROSS_SUFFIX=$(CROSS_COMPILE) +OPENBLAS_BUILD_OPTS += OSNAME=$(OS) CROSS=1 HOSTCC="$(HOSTCC)" CROSS_SUFFIX=$(CROSS_COMPILE) endif ifeq ($(OS),WINNT) ifneq ($(ARCH),x86_64) diff --git a/deps/openssl.mk b/deps/openssl.mk index 734ddb3274e78..705303432c2c6 100644 --- a/deps/openssl.mk +++ b/deps/openssl.mk @@ -59,7 +59,7 @@ $(BUILDDIR)/openssl-$(OPENSSL_VER)/build-configured: $(SRCCACHE)/openssl-$(OPENS mkdir -p $(dir $@) cd $(dir $@) && \ CC="$(CC) $(SANITIZE_OPTS)" CXX="$(CXX) $(SANITIZE_OPTS)" LDFLAGS="$(LDFLAGS) $(RPATH_ESCAPED_ORIGIN) $(SANITIZE_LDFLAGS)" \ - $(dir $<)/Configure shared --prefix=$(abspath $(build_prefix)) --libdir=$(abspath $(build_libdir)) $(OPENSSL_TARGET) + $(dir $<)/Configure shared --prefix=$(abspath $(build_prefix)) $(OPENSSL_TARGET) echo 1 > $@ $(BUILDDIR)/openssl-$(OPENSSL_VER)/build-compiled: $(BUILDDIR)/openssl-$(OPENSSL_VER)/build-configured @@ -75,16 +75,18 @@ endif # Override bindir and only install runtime libraries, otherwise they'll go into build_depsbindir. OPENSSL_INSTALL = \ mkdir -p $2$$(build_shlibdir) && \ - $$(MAKE) -C $1 install_runtime_libs $$(MAKE_COMMON) bindir=$$(build_shlibdir) $3 DESTDIR="$2" + $$(MAKE) -C $1 install_dev $$(MAKE_COMMON) bindir=$$(build_shlibdir) $3 DESTDIR="$2" + +OPENSSL_POST_INSTALL := \ + $(WIN_MAKE_HARD_LINK) $(build_bindir)/libcrypto-*.dll $(build_bindir)/libcrypto.dll && \ + $(WIN_MAKE_HARD_LINK) $(build_bindir)/libssl-*.dll $(build_bindir)/libssl.dll && \ + $(INSTALL_NAME_CMD)libcrypto.$(SHLIB_EXT) $(build_shlibdir)/libcrypto.$(SHLIB_EXT) && \ + $(INSTALL_NAME_CMD)libssl.$(SHLIB_EXT) $(build_shlibdir)/libssl.$(SHLIB_EXT) && \ + $(INSTALL_NAME_CHANGE_CMD) $(build_shlibdir)/libcrypto.3.dylib @rpath/libcrypto.$(SHLIB_EXT) $(build_shlibdir)/libssl.$(SHLIB_EXT) $(eval $(call staged-install, \ openssl,openssl-$(OPENSSL_VER), \ - OPENSSL_INSTALL,,, \ - $$(WIN_MAKE_HARD_LINK) $(build_bindir)/libcrypto-*.dll $(build_bindir)/libcrypto.dll && \ - $$(WIN_MAKE_HARD_LINK) $(build_bindir)/libssl-*.dll $(build_bindir)/libssl.dll && \ - $$(INSTALL_NAME_CMD)libcrypto.$$(SHLIB_EXT) $$(build_shlibdir)/libcrypto.$$(SHLIB_EXT) && \ - $$(INSTALL_NAME_CMD)libssl.$$(SHLIB_EXT) $$(build_shlibdir)/libssl.$$(SHLIB_EXT) && \ - $$(INSTALL_NAME_CHANGE_CMD) $$(build_shlibdir)/libcrypto.3.dylib @rpath/libcrypto.$$(SHLIB_EXT) $$(build_shlibdir)/libssl.$$(SHLIB_EXT))) + OPENSSL_INSTALL,,,$(OPENSSL_POST_INSTALL))) clean-openssl: -rm -f $(BUILDDIR)/-openssl-$(OPENSSL_VER)/build-configured $(BUILDDIR)/-openssl-$(OPENSSL_VER)/build-compiled diff --git a/deps/tools/common.mk b/deps/tools/common.mk index 212b576fb63f9..01b57316f9d1a 100644 --- a/deps/tools/common.mk +++ b/deps/tools/common.mk @@ -59,6 +59,7 @@ CMAKE_COMMON += -DCMAKE_SYSTEM_NAME=Windows CMAKE_COMMON += -DCMAKE_RC_COMPILER="$$(which $(CROSS_COMPILE)windres)" endif +# For now this is LLVM specific, but I expect it won't be in the future ifeq ($(CMAKE_GENERATOR),Ninja) CMAKE_GENERATOR_COMMAND := -G Ninja else ifeq ($(CMAKE_GENERATOR),make) @@ -67,27 +68,6 @@ else $(error Unknown CMake generator '$(CMAKE_GENERATOR)'. Options are 'Ninja' and 'make') endif -ifneq (,$(findstring MINGW,$(RAW_BUILD_OS))) -ifneq (,$(shell ldd $(shell which cmake) | grep msys-2.0.dll)) -# Detect MSYS2 with cygwin CMake rather than MinGW cmake - the former fails to -# properly drive MinGW tools -override CMAKE := echo "ERROR: CMake is Cygwin CMake, not MinGW CMake. Build will fail. Use 'pacman -S mingw-w64-{i686,x86_64}-cmake'."; exit 1; $(CMAKE) -endif -# In our setup, CMAKE_INSTALL_PREFIX is a relative path inside usr-staging. -# We do not want this converted to a windows path, because our make system -# assumes it to be relative to msys `/`. -override CMAKE := MSYS2_ARG_CONV_EXCL="-DCMAKE_INSTALL_PREFIX" $(CMAKE) -endif - -# Some dependencies' tarballs contains symlinks to non-existent targets. This breaks the -# the default msys strategy `deepcopy` symlink strategy. To workaround this, -# switch to `native` which tries native windows symlinks (possible if the -# machine is in developer mode) - or if not, falls back to cygwin-style -# symlinks. We don't particularly care either way - we just need to symlinks -# to succeed. We could guard this by a uname check, but it's harmless elsewhere, -# so let's not incur the additional overhead. -MSYS_NONEXISTENT_SYMLINK_TARGET_FIX := export MSYS=winsymlinks:native - # If the top-level Makefile is called with environment variables, # they will override the values passed above to ./configure MAKE_COMMON := DESTDIR="" prefix=$(build_prefix) bindir=$(build_depsbindir) libdir=$(build_libdir) shlibdir=$(build_shlibdir) libexecdir=$(build_libexecdir) datarootdir=$(build_datarootdir) includedir=$(build_includedir) sysconfdir=$(build_sysconfdir) O= @@ -164,7 +144,7 @@ upper = $(shell echo $1 | tr a-z A-Z) # this rule ensures that make install is more nearly atomic # so it's harder to get half-installed (or half-reinstalled) dependencies # # and enables sharing deps compiles, uninstall, and fast reinstall -MAKE_INSTALL = MSYS2_ARG_CONV_EXCL="prefix=" $$(MAKE) -C $1 install $$(MAKE_COMMON) $3 DESTDIR="$2" +MAKE_INSTALL = +$$(MAKE) -C $1 install $$(MAKE_COMMON) $3 DESTDIR="$2" define SHLIBFILE_INSTALL mkdir -p $2/$$(build_shlibdir) 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) diff --git a/pkgimage.mk b/pkgimage.mk index 78b2618be549f..e90e0e9618808 100644 --- a/pkgimage.mk +++ b/pkgimage.mk @@ -26,7 +26,7 @@ print-depot-path: $(BUILDDIR)/stdlib/%.image: $(JULIAHOME)/stdlib/Project.toml $(JULIAHOME)/stdlib/Manifest.toml $(INDEPENDENT_STDLIBS_SRCS) $(JULIA_DEPOT_PATH)/compiled @$(call PRINT_JULIA, JULIA_CPU_TARGET="$(JULIA_CPU_TARGET)" $(call spawn,$(JULIA_EXECUTABLE)) --startup-file=no -e \ - 'Base.Precompilation.precompilepkgs(configs=[``=>Base.CacheFlags(debug_level=2, opt_level=3), ``=>Base.CacheFlags(check_bounds=1, debug_level=2, opt_level=3)])') + 'Base.Precompilation.precompilepkgs(configs=[``=>Base.CacheFlags(debug_level=2, opt_level=3), ``=>Base.CacheFlags(check_bounds=1, debug_level=2, opt_level=3)]; strict=true)') touch $@ $(BUILDDIR)/stdlib/release.image: $(build_private_libdir)/sys.$(SHLIB_EXT) diff --git a/src/Makefile b/src/Makefile index 6245139592db1..e859acc765354 100644 --- a/src/Makefile +++ b/src/Makefile @@ -75,7 +75,7 @@ GC_CODEGEN_SRCS += llvm-late-gc-lowering-stock endif CODEGEN_SRCS := codegen jitlayers aotcompile debuginfo disasm llvm-simdloop \ llvm-pass-helpers llvm-ptls \ - llvm-lower-handlers llvm-propagate-addrspaces \ + llvm-lower-handlers llvm-propagate-addrspaces null_sysimage \ llvm-multiversioning llvm-alloc-opt llvm-alloc-helpers cgmemmgr llvm-remove-addrspaces \ llvm-remove-ni llvm-julia-licm llvm-demote-float16 llvm-cpufeatures pipeline llvm_api \ $(GC_CODEGEN_SRCS) @@ -192,9 +192,7 @@ endif COMMON_LIBPATHS := -L$(build_libdir) -L$(build_shlibdir) RT_LIBS := $(WHOLE_ARCHIVE) $(LIBUV) $(WHOLE_ARCHIVE) $(LIBUTF8PROC) $(NO_WHOLE_ARCHIVE) $(LIBUNWIND) $(RT_LLVMLINK) $(OSLIBS) $(LIBTRACYCLIENT) $(LIBITTAPI) $(MMTK_LIB) -# NB: CG needs uv_mutex_* symbols, but we expect to export them from libjulia-internal CG_LIBS := $(LIBUNWIND) $(CG_LLVMLINK) $(OSLIBS) $(LIBTRACYCLIENT) $(LIBITTAPI) $(MMTK_LIB) - RT_DEBUG_LIBS := $(COMMON_LIBPATHS) $(WHOLE_ARCHIVE) $(BUILDDIR)/flisp/libflisp-debug.a $(WHOLE_ARCHIVE) $(BUILDDIR)/support/libsupport-debug.a -ljulia-debug $(RT_LIBS) CG_DEBUG_LIBS := $(COMMON_LIBPATHS) $(CG_LIBS) -ljulia-debug -ljulia-internal-debug RT_RELEASE_LIBS := $(COMMON_LIBPATHS) $(WHOLE_ARCHIVE) $(BUILDDIR)/flisp/libflisp.a $(WHOLE_ARCHIVE) $(BUILDDIR)/support/libsupport.a -ljulia $(RT_LIBS) @@ -471,10 +469,10 @@ libjulia-codegen-debug: $(build_shlibdir)/libjulia-codegen-debug.$(JL_MAJOR_MINO libjulia-codegen-debug libjulia-codegen-release: $(PUBLIC_HEADER_TARGETS) # set the exports for the source files based on where they are getting linked -$(OBJS): SHIPFLAGS += -DJL_LIBRARY_EXPORTS_INTERNAL -DBUILDING_UV_SHARED -$(DOBJS): DEBUGFLAGS += -DJL_LIBRARY_EXPORTS_INTERNAL -DBUILDING_UV_SHARED -$(CODEGEN_OBJS): SHIPFLAGS += -DJL_LIBRARY_EXPORTS_CODEGEN -DUSING_UV_SHARED -$(CODEGEN_DOBJS): DEBUGFLAGS += -DJL_LIBRARY_EXPORTS_CODEGEN -DUSING_UV_SHARED +$(OBJS): SHIPFLAGS += -DJL_LIBRARY_EXPORTS_INTERNAL +$(DOBJS): DEBUGFLAGS += -DJL_LIBRARY_EXPORTS_INTERNAL +$(CODEGEN_OBJS): SHIPFLAGS += -DJL_LIBRARY_EXPORTS_CODEGEN +$(CODEGEN_DOBJS): DEBUGFLAGS += -DJL_LIBRARY_EXPORTS_CODEGEN clean: -rm -fr $(build_shlibdir)/libjulia-internal* $(build_shlibdir)/libjulia-codegen* $(build_shlibdir)/libccalltest* $(build_shlibdir)/libllvmcalltest* diff --git a/src/aotcompile.cpp b/src/aotcompile.cpp index 6009bd435534c..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) { @@ -124,6 +142,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/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 04f38fb9091be..c77c449fa1b3d 100644 --- a/src/codegen-stubs.c +++ b/src/codegen-stubs.c @@ -14,9 +14,10 @@ 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 - +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 @@ -116,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 8f0644c0cff7f..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) @@ -2305,6 +2307,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); @@ -5007,7 +5022,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++; @@ -6159,6 +6178,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) { @@ -7855,7 +7875,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); @@ -7867,6 +7887,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())); @@ -7931,7 +7952,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; } @@ -9334,12 +9355,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/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; } diff --git a/src/flisp/Makefile b/src/flisp/Makefile index 2e902a31dbeed..eca1de86e588a 100644 --- a/src/flisp/Makefile +++ b/src/flisp/Makefile @@ -117,7 +117,7 @@ $(BUILDDIR)/host/Makefile: @printf "%s\n" 'include $(SRCDIR)/Makefile' >> $@ $(BUILDDIR)/host/$(EXENAME): $(BUILDDIR)/host/Makefile | ${BUILDDIR}/host/flisp.boot - $(MAKE) -C $(BUILDDIR)/host $(EXENAME) + make -C $(BUILDDIR)/host $(EXENAME) $(BUILDDIR)/host/flisp.boot: $(SRCDIR)/flisp.boot | $(BUILDDIR)/host/Makefile diff --git a/src/gc-interface.h b/src/gc-interface.h index 826e91355b17a..9c59e274d1261 100644 --- a/src/gc-interface.h +++ b/src/gc-interface.h @@ -240,7 +240,14 @@ STATIC_INLINE void jl_gc_wb(const void *parent, const void *ptr) JL_NOTSAFEPOINT // so write barriers can be omitted until the next allocation. 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_fresh(const void *parent, const void *ptr) JL_NOTSAFEPOINT {} +STATIC_INLINE void jl_gc_wb_fresh(const void *parent JL_UNUSED, const void *ptr JL_UNUSED) 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/gc-pages.c b/src/gc-pages.c index 71d59de29166f..79dd8993a8861 100644 --- a/src/gc-pages.c +++ b/src/gc-pages.c @@ -28,6 +28,28 @@ JL_DLLEXPORT uint64_t jl_get_pg_size(void) static int block_pg_cnt = DEFAULT_BLOCK_PG_ALLOC; +// Julia allocates large blocks (64M) with mmap. These are never +// unmapped but the underlying physical memory may be released +// with calls to madvise(MADV_DONTNEED). +static uint64_t poolmem_blocks_allocated_total = 0; + +JL_DLLEXPORT uint64_t jl_poolmem_blocks_allocated_total(void) +{ + return poolmem_blocks_allocated_total; +} + +JL_DLLEXPORT uint64_t jl_poolmem_bytes_allocated(void) +{ + return jl_atomic_load_relaxed(&gc_heap_stats.bytes_resident); +} + +JL_DLLEXPORT uint64_t jl_current_pg_count(void) +{ + assert(jl_page_size == GC_PAGE_SZ && "RAI fork of Julia should be running on platforms for which jl_page_size == GC_PAGE_SZ"); + size_t nb = jl_atomic_load_relaxed(&gc_heap_stats.bytes_resident); + return nb / GC_PAGE_SZ; // exact division +} + void jl_gc_init_page(void) { if (GC_PAGE_SZ * block_pg_cnt < jl_page_size) @@ -55,6 +77,11 @@ char *jl_gc_try_alloc_pages_(int pg_cnt) JL_NOTSAFEPOINT MAP_NORESERVE | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if (mem == MAP_FAILED) return NULL; + +#ifdef MADV_NOHUGEPAGE + madvise(mem, pages_sz, MADV_NOHUGEPAGE); +#endif + #endif if (GC_PAGE_SZ > jl_page_size) // round data pointer up to the nearest gc_page_data-aligned @@ -62,6 +89,7 @@ char *jl_gc_try_alloc_pages_(int pg_cnt) JL_NOTSAFEPOINT mem = (char*)gc_page_data(mem + GC_PAGE_SZ - 1); jl_atomic_fetch_add_relaxed(&gc_heap_stats.bytes_mapped, pages_sz); jl_atomic_fetch_add_relaxed(&gc_heap_stats.bytes_resident, pages_sz); + poolmem_blocks_allocated_total++; // RAI-specific return mem; } @@ -184,7 +212,7 @@ void jl_gc_free_page(jl_gc_pagemeta_t *pg) JL_NOTSAFEPOINT } #ifdef _OS_WINDOWS_ VirtualFree(p, decommit_size, MEM_DECOMMIT); -#elif defined(MADV_FREE) +#elif 0 static int supports_madv_free = 1; if (supports_madv_free) { if (madvise(p, decommit_size, MADV_FREE) == -1) { diff --git a/src/gc-stacks.c b/src/gc-stacks.c index 9387c7fb065ec..55c4a470dd33e 100644 --- a/src/gc-stacks.c +++ b/src/gc-stacks.c @@ -72,8 +72,10 @@ static void *malloc_stack(size_t bufsz) JL_NOTSAFEPOINT munmap(stk, bufsz); return MAP_FAILED; } -# endif - +#ifdef MADV_NOHUGEPAGE + madvise(stk, bufsz, MADV_NOHUGEPAGE); +#endif +#endif jl_atomic_fetch_add_relaxed(&num_stack_mappings, 1); return stk; } diff --git a/src/gc-stock.c b/src/gc-stock.c index 55f31b26679ff..a56e61cdec37f 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; @@ -629,7 +630,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); @@ -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; } @@ -3397,6 +3407,9 @@ static int _jl_gc_collect(jl_ptls_t ptls, jl_gc_collection_t collection) return recollect; } +extern int jl_heartbeat_pause(void); +extern int jl_heartbeat_resume(void); + JL_DLLEXPORT void jl_gc_collect(jl_gc_collection_t collection) { JL_PROBE_GC_BEGIN(collection); @@ -3439,6 +3452,7 @@ JL_DLLEXPORT void jl_gc_collect(jl_gc_collection_t collection) // existence of the thread in the jl_n_threads count. // // TODO: concurrently queue objects + jl_heartbeat_pause(); jl_fence(); gc_n_threads = jl_atomic_load_acquire(&jl_n_threads); gc_all_tls_states = jl_atomic_load_relaxed(&jl_all_tls_states); @@ -3470,6 +3484,7 @@ JL_DLLEXPORT void jl_gc_collect(jl_gc_collection_t collection) gc_n_threads = 0; gc_all_tls_states = NULL; + jl_heartbeat_resume(); jl_safepoint_end_gc(); jl_gc_state_set(ptls, old_state, JL_GC_STATE_WAITING); JL_PROBE_GC_END(); @@ -3906,6 +3921,9 @@ void *jl_gc_perm_alloc_nolock(size_t sz, int zero, unsigned align, unsigned offs errno = last_errno; if (__unlikely(pool == MAP_FAILED)) return NULL; +#ifdef MADV_NOHUGEPAGE + madvise(pool, GC_PERM_POOL_SIZE, MADV_NOHUGEPAGE); +#endif #endif gc_perm_pool = (uintptr_t)pool; gc_perm_end = gc_perm_pool + GC_PERM_POOL_SIZE; diff --git a/src/gc-stock.h b/src/gc-stock.h index d478ee1366da0..fb55a2e8fa9bd 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 @@ -499,6 +500,9 @@ extern uv_sem_t gc_sweep_assists_needed; extern _Atomic(int) gc_n_threads_marking; extern _Atomic(int) gc_n_threads_sweeping_pools; extern _Atomic(int) n_threads_running; +extern _Atomic(int) gc_n_threads_sweeping_stacks; +extern _Atomic(int) gc_ptls_sweep_idx; +extern _Atomic(int) gc_stack_free_idx; extern uv_barrier_t thread_init_done; void gc_mark_queue_all_roots(jl_ptls_t ptls, jl_gc_markqueue_t *mq); void gc_mark_finlist_(jl_gc_markqueue_t *mq, jl_value_t *fl_parent, jl_value_t **fl_begin, jl_value_t **fl_end) JL_NOTSAFEPOINT; 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') diff --git a/src/gf.c b/src/gf.c index 47db5474701ac..53415ba4716fd 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; @@ -1841,7 +1845,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 +1862,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 +3083,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 +3098,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: @@ -3890,6 +3894,15 @@ JL_DLLEXPORT void jl_compile_method_sig(jl_method_t *m, jl_value_t *types, jl_sv jl_compile_method_instance(mi, NULL, world); } +JL_DLLEXPORT int jl_is_compilable(jl_tupletype_t *types) +{ + size_t world = jl_atomic_load_acquire(&jl_world_counter); + size_t min_valid = 0; + size_t max_valid = ~(size_t)0; + jl_method_instance_t *mi = jl_get_compile_hint_specialization(types, world, &min_valid, &max_valid, 1); + return mi == NULL ? 0 : 1; +} + JL_DLLEXPORT int jl_compile_hint(jl_tupletype_t *types) { size_t world = jl_atomic_load_acquire(&jl_world_counter); @@ -4169,7 +4182,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/src/init.c b/src/init.c index e11b360b5d378..f9c5409a0fe99 100644 --- a/src/init.c +++ b/src/init.c @@ -552,6 +552,9 @@ 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; + +extern void jl_init_heartbeat(void); static void restore_fp_env(void) { @@ -612,6 +615,11 @@ static NOINLINE void _finish_jl_init_(jl_image_buf_t sysimage, jl_ptls_t ptls, j jl_start_gc_threads(); uv_barrier_wait(&thread_init_done); + if (jl_base_module != NULL) { + // requires code in Base + jl_init_heartbeat(); + } + jl_gc_enable(1); if ((sysimage.kind != JL_IMAGE_KIND_NONE) && @@ -661,6 +669,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) @@ -750,6 +759,15 @@ JL_DLLEXPORT void jl_init_(jl_image_buf_t sysimage) if (jl_options.handle_signals == JL_OPTIONS_HANDLE_SIGNALS_ON) jl_install_default_signal_handlers(); +#if (defined(_OS_LINUX_) && defined(_CPU_X86_64_)) || (defined(_OS_DARWIN_) && defined(_CPU_AARCH64_)) + if (jl_options.safe_crash_log_file != NULL) { + jl_sig_fd = open(jl_options.safe_crash_log_file, O_WRONLY | O_CREAT | O_APPEND, 0600); + if (jl_sig_fd == -1) { + jl_error("fatal error: could not open safe crash log file for writing"); + } + } +#endif + jl_gc_init(); arraylist_new(&jl_linkage_blobs, 0); 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/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 { diff --git a/src/jl_exported_funcs.inc b/src/jl_exported_funcs.inc index 61420c7306de9..aee7f0974359f 100644 --- a/src/jl_exported_funcs.inc +++ b/src/jl_exported_funcs.inc @@ -271,6 +271,7 @@ XX(jl_istopmod) \ XX(jl_is_binding_deprecated) \ XX(jl_is_char_signed) \ + XX(jl_is_compilable) \ XX(jl_is_const) \ XX(jl_is_assertsbuild) \ XX(jl_is_debugbuild) \ @@ -523,8 +524,10 @@ 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_get_llvm_cis) \ YY(jl_dump_function_asm) \ YY(jl_LLVMCreateDisasm) \ YY(jl_LLVMDisasmInstruction) \ @@ -542,6 +545,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) \ diff --git a/src/jl_uv.c b/src/jl_uv.c index a21b05433b8c6..5d8ba54c06c4e 100644 --- a/src/jl_uv.c +++ b/src/jl_uv.c @@ -15,6 +15,7 @@ #include "errno.h" #include #include +#include #endif #include "julia.h" @@ -813,6 +814,83 @@ JL_DLLEXPORT int jl_printf(uv_stream_t *s, const char *format, ...) return c; } +STATIC_INLINE int copystp(char *dest, const char *src) +{ + char *d = stpcpy(dest, src); + return (int)(d - dest); +} + +// RAI-specific +STATIC_INLINE void write_to_safe_crash_log(char *buf) JL_NOTSAFEPOINT +{ + int buflen = strlen(buf); + // Our telemetry on SPCS expects a JSON object per line. + // We ignore write failures because there is nothing we can do. + // We'll use a 2K byte buffer: 69 bytes for JSON message decorations, + // 1 byte for the terminating NUL character, and 3 bytes for an + // ellipsis if we have to truncate the message leaves `max_b` bytes + // for the message. + const int wbuflen = 2048; + const int max_b = wbuflen - 70 - 3; + char wbuf[wbuflen]; + bzero(wbuf, wbuflen); + int wlen = 0; + + // JSON preamble (32 bytes) + wlen += copystp(&wbuf[wlen], "\n{\"level\":\"Error\", \"timestamp\":\""); + + // Timestamp (19 bytes) + struct timeval tv; + struct tm* tm_info; + gettimeofday(&tv, NULL); + tm_info = gmtime(&tv.tv_sec); + wlen += strftime(&wbuf[wlen], 42, "%Y-%m-%dT%H:%M:%S", tm_info); + sprintf(&wbuf[wlen], ".%03ld", (long)tv.tv_usec / 1000); + wlen += 4; + + // JSON preamble to message (15 bytes) + wlen += copystp(&wbuf[wlen], "\", \"message\": \""); + + // Message + // Each iteration will advance wlen by 1 or 2 + for (size_t i = 0; i < buflen; i++) { + // Truncate the message if the write buffer is full + if (wlen == max_b || wlen == max_b - 1) { + wlen += copystp(&wbuf[wlen], "..."); + break; + } + switch (buf[i]) { + case '"': + wlen += copystp(&wbuf[wlen], "\\\""); + break; + case '\b': + wlen += copystp(&wbuf[wlen], "\\b"); + break; + case '\n': + wlen += copystp(&wbuf[wlen], "\\n"); + break; + case '\r': + wlen += copystp(&wbuf[wlen], "\\r"); + break; + case '\t': + wlen += copystp(&wbuf[wlen], "\\t"); + break; + case '\\': + wlen += copystp(&wbuf[wlen], "\\\\"); + break; + default: + wbuf[wlen++] = buf[i]; + break; + } + } + // JSON completion (3 bytes) + wlen += copystp(&wbuf[wlen], "\"}\n"); + write(jl_sig_fd, wbuf, wlen); + fdatasync(jl_sig_fd); +} + +extern int jl_inside_heartbeat_thread(void); + JL_DLLEXPORT void jl_safe_printf(const char *fmt, ...) { static char buf[1000]; @@ -829,6 +907,12 @@ JL_DLLEXPORT void jl_safe_printf(const char *fmt, ...) va_end(args); buf[999] = '\0'; + // order is important here: we want to ensure that the threading infra + // has been initialized before we start trying to print to the + // safe crash log file + if (jl_sig_fd != 0 && (jl_inside_signal_handler() || jl_inside_heartbeat_thread())) { + write_to_safe_crash_log(buf); + } if (write(STDERR_FILENO, buf, strlen(buf)) < 0) { // nothing we can do; ignore the failure } diff --git a/src/jloptions.c b/src/jloptions.c index 96cce1c8d29a3..e128045b5b5af 100644 --- a/src/jloptions.c +++ b/src/jloptions.c @@ -155,6 +155,7 @@ JL_DLLEXPORT void jl_init_options(void) JL_TRIM_NO, // trim 0, // task_metrics -1, // timeout_for_safepoint_straggler_s + NULL, // safe_crash_log_file }; jl_options_initialized = 1; } @@ -384,6 +385,7 @@ JL_DLLEXPORT void jl_parse_opts(int *argcp, char ***argvp) opt_permalloc_pkgimg, opt_trim, opt_experimental_features, + opt_safe_crash_log_file, }; static const char* const shortopts = "+vhqH:e:E:L:J:C:it:p:O:g:m:"; static const struct option longopts[] = { @@ -452,6 +454,7 @@ JL_DLLEXPORT void jl_parse_opts(int *argcp, char ***argvp) { "permalloc-pkgimg",required_argument, 0, opt_permalloc_pkgimg }, { "heap-size-hint", required_argument, 0, opt_heap_size_hint }, { "trim", optional_argument, 0, opt_trim }, + { "safe-crash-log-file", required_argument, 0, opt_safe_crash_log_file }, { 0, 0, 0, 0 } }; @@ -668,9 +671,11 @@ JL_DLLEXPORT void jl_parse_opts(int *argcp, char ***argvp) } jl_options.nthreads = nthreads + nthreadsi; } + assert(jl_options.nthreadpools == 1 || jl_options.nthreadpools == 2); int16_t *ntpp = (int16_t *)malloc_s(jl_options.nthreadpools * sizeof(int16_t)); ntpp[0] = (int16_t)nthreads; - ntpp[1] = (int16_t)nthreadsi; + if (jl_options.nthreadpools == 2) + ntpp[1] = (int16_t)nthreadsi; jl_options.nthreads_per_pool = ntpp; break; case 'p': // procs @@ -1011,6 +1016,10 @@ JL_DLLEXPORT void jl_parse_opts(int *argcp, char ***argvp) jl_options.task_metrics = JL_OPTIONS_TASK_METRICS_ON; else jl_errorf("julia: invalid argument to --task-metrics={yes|no} (%s)", optarg); + case opt_safe_crash_log_file: + jl_options.safe_crash_log_file = strdup(optarg); + if (jl_options.safe_crash_log_file == NULL) + jl_error("julia: failed to allocate memory for --safe-crash-log-file"); break; default: jl_errorf("julia: unhandled option -- %c\n" diff --git a/src/jloptions.h b/src/jloptions.h index 06e00e9309dba..f5c8f72a2cb6c 100644 --- a/src/jloptions.h +++ b/src/jloptions.h @@ -68,6 +68,7 @@ typedef struct { int8_t trim; int8_t task_metrics; int16_t timeout_for_safepoint_straggler_s; + const char *safe_crash_log_file; } jl_options_t; #endif diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index aad8105b34308..07668878a20e1 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -1790,11 +1790,13 @@ (cons e '()) (let ((a '())) (define (arg-to-temp x) - (cond ((effect-free? x) x) - ((or (eq? (car x) '...) (eq? (car x) '&)) - `(,(car x) ,(arg-to-temp (cadr x)))) + (cond ((effect-free? x) x) + ((eq? (car x) '...) + `(... ,(arg-to-temp (cadr x)))) ((eq? (car x) 'kw) - `(,(car x) ,(cadr x) ,(arg-to-temp (caddr x)))) + `(kw ,(cadr x) ,(arg-to-temp (caddr x)))) + ((eq? (car x) 'parameters) + `(parameters ,@(map arg-to-temp (cdr x)))) (else (let ((g (make-ssavalue))) (begin (set! a (cons `(= ,g ,x) a)) diff --git a/src/julia_internal.h b/src/julia_internal.h index c9e1b0e204df6..a8172c3e8c1c1 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -230,6 +230,7 @@ extern volatile size_t profile_bt_size_max; extern volatile size_t profile_bt_size_cur; extern volatile int profile_running; extern volatile int profile_all_tasks; +extern int heartbeat_tid; // Mostly used to ensure we skip this thread in the CPU profiler. XXX: not implemented on Windows // Ensures that we can safely read the `live_tasks`field of every TLS when profiling. // We want to avoid the case that a GC gets interleaved with `jl_profile_task` and shrinks // the `live_tasks` array while we are reading it or frees tasks that are being profiled. @@ -245,6 +246,7 @@ extern uv_mutex_t bt_data_prof_lock; #define PROFILE_STATE_THREAD_NOT_SLEEPING (1) #define PROFILE_STATE_THREAD_SLEEPING (2) #define PROFILE_STATE_WALL_TIME_PROFILING (3) +extern _Atomic(int) n_threads_running; void jl_profile_task(void); // number of cycles since power-on @@ -780,6 +782,32 @@ JL_CALLABLE(jl_f_tuple); void jl_install_default_signal_handlers(void); void restore_signals(void); void jl_install_thread_signal_handler(jl_ptls_t ptls); +extern const size_t sig_stack_size; +STATIC_INLINE int is_addr_on_sigstack(jl_ptls_t ptls, void *ptr) +{ + // One guard page for signal_stack. + return !((char*)ptr < (char*)ptls->signal_stack - jl_page_size || + (char*)ptr > (char*)ptls->signal_stack + sig_stack_size); +} +STATIC_INLINE int jl_inside_signal_handler(void) +{ +#if (defined(_OS_LINUX_) && defined(_CPU_X86_64_)) || (defined(_OS_DARWIN_) && defined(_CPU_AARCH64_)) + // Read the stack pointer + size_t sp; +#if defined(_OS_LINUX_) && defined(_CPU_X86_64_) + __asm__ __volatile__("movq %%rsp, %0" : "=r"(sp)); +#elif defined(_OS_DARWIN_) && defined(_CPU_AARCH64_) + __asm__ __volatile__("mov %0, sp" : "=r"(sp)); +#endif + // Check if the stack pointer is within the signal stack + jl_ptls_t ptls = jl_current_task->ptls; + return is_addr_on_sigstack(ptls, (void*)sp); +#else + return 0; +#endif +} +// File-descriptor for safe logging on signal handling +extern int jl_sig_fd; extern uv_loop_t *jl_io_loop; JL_DLLEXPORT void jl_uv_flush(uv_stream_t *stream); @@ -1499,9 +1527,9 @@ JL_DLLEXPORT jl_value_t *jl_backtrace_from_here(int returnsp, int skip); void jl_critical_error(int sig, int si_code, bt_context_t *context, jl_task_t *ct); JL_DLLEXPORT void jl_raise_debugger(void) JL_NOTSAFEPOINT; JL_DLLEXPORT void jl_gdblookup(void* ip) JL_NOTSAFEPOINT; -void jl_print_native_codeloc(uintptr_t ip) JL_NOTSAFEPOINT; -void jl_print_bt_entry_codeloc(jl_bt_element_t *bt_data) JL_NOTSAFEPOINT; -JL_DLLEXPORT void jl_print_task_backtraces(int show_done) JL_NOTSAFEPOINT; +void jl_print_native_codeloc(char *pre_str, uintptr_t ip) JL_NOTSAFEPOINT; +void jl_print_bt_entry_codeloc(int sig, jl_bt_element_t *bt_data) JL_NOTSAFEPOINT; +JL_DLLEXPORT void jl_print_task_backtraces(int show_done)JL_NOTSAFEPOINT ; #ifdef _OS_WINDOWS_ JL_DLLEXPORT void jl_refresh_dbg_module_list(void); #endif @@ -2057,6 +2085,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, @@ -2064,7 +2093,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/llvm-alloc-opt.cpp b/src/llvm-alloc-opt.cpp index ce1d22f42d0ae..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); @@ -701,6 +702,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) { @@ -958,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; @@ -973,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/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)) { 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/src/options.h b/src/options.h index 0715069faab32..fb2797ffd0336 100644 --- a/src/options.h +++ b/src/options.h @@ -144,6 +144,9 @@ #define MACHINE_EXCLUSIVE_NAME "JULIA_EXCLUSIVE" #define DEFAULT_MACHINE_EXCLUSIVE 0 +// heartbeats +#define JL_HEARTBEAT_THREAD + // sanitizer defaults --------------------------------------------------------- // Automatically enable MEMDEBUG and KEEP_BODIES for the sanitizers 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/safepoint.c b/src/safepoint.c index 970c48875d790..96da3c1a05eb1 100644 --- a/src/safepoint.c +++ b/src/safepoint.c @@ -172,7 +172,7 @@ void jl_gc_wait_for_the_world(jl_ptls_t* gc_all_tls_states, int gc_n_threads) size_t bt_size = jl_try_record_thread_backtrace(ptls2, ptls->bt_data, JL_MAX_BT_SIZE); // Print the backtrace of the straggler for (size_t i = 0; i < bt_size; i += jl_bt_entry_size(ptls->bt_data + i)) { - jl_print_bt_entry_codeloc(ptls->bt_data + i); + jl_print_bt_entry_codeloc(-1, ptls->bt_data + i); } } } diff --git a/src/signal-handling.c b/src/signal-handling.c index b03f0c1a430cd..269885bb5c14c 100644 --- a/src/signal-handling.c +++ b/src/signal-handling.c @@ -30,6 +30,8 @@ static const uint64_t GIGA = 1000000000ULL; // Timers to take samples at intervals JL_DLLEXPORT void jl_profile_stop_timer(void); JL_DLLEXPORT int jl_profile_start_timer(uint8_t); +// File-descriptor for safe logging on signal handling +int jl_sig_fd; /////////////////////// // Utility functions // @@ -647,7 +649,7 @@ void jl_critical_error(int sig, int si_code, bt_context_t *context, jl_task_t *c *bt_size = n = rec_backtrace_ctx(bt_data, JL_MAX_BT_SIZE, context, NULL); } for (i = 0; i < n; i += jl_bt_entry_size(bt_data + i)) { - jl_print_bt_entry_codeloc(bt_data + i); + jl_print_bt_entry_codeloc(sig, bt_data + i); } jl_gc_debug_print_status(); jl_gc_debug_critical_error(); diff --git a/src/signals-mach.c b/src/signals-mach.c index 4a670547bdfcd..12d545ee81ce6 100644 --- a/src/signals-mach.c +++ b/src/signals-mach.c @@ -824,6 +824,10 @@ void *mach_profile_listener(void *arg) for (int idx = nthreads; idx-- > 0; ) { // Stop the threads in random order. int i = randperm[idx]; + // skip heartbeat thread + if (i == heartbeat_tid) { + continue; + } jl_profile_thread_mach(i); } } diff --git a/src/signals-unix.c b/src/signals-unix.c index 2db397050420e..ed186d5b357cd 100644 --- a/src/signals-unix.c +++ b/src/signals-unix.c @@ -42,7 +42,7 @@ #endif // 8M signal stack, same as default stack size (though we barely use this) -static const size_t sig_stack_size = 8 * 1024 * 1024; +const size_t sig_stack_size = 8 * 1024 * 1024; #include "julia_assert.h" @@ -102,14 +102,6 @@ static inline uintptr_t jl_get_rsp_from_ctx(const void *_ctx) #endif } -static int is_addr_on_sigstack(jl_ptls_t ptls, void *ptr) JL_NOTSAFEPOINT -{ - // One guard page for signal_stack. - return ptls->signal_stack == NULL || - ((char*)ptr >= (char*)ptls->signal_stack - jl_page_size && - (char*)ptr <= (char*)ptls->signal_stack + (ptls->signal_stack_size ? ptls->signal_stack_size : sig_stack_size)); -} - // Modify signal context `_ctx` so that `fptr` will execute when the signal returns // The function `fptr` itself must not return. JL_NO_ASAN static void jl_call_in_ctx(jl_ptls_t ptls, void (*fptr)(void), int sig, void *_ctx) @@ -873,6 +865,10 @@ static void do_profile(void *ctx) for (int idx = nthreads; idx-- > 0; ) { // Stop the threads in the random order. int tid = randperm[idx]; + // skip heartbeat thread + if (tid == heartbeat_tid) { + return; + } // do backtrace for profiler if (!profile_running) return; @@ -1111,7 +1107,7 @@ static void *signal_listener(void *arg) jl_safe_printf("\nsignal (%d): %s\n", sig, strsignal(sig)); size_t i; for (i = 0; i < signal_bt_size; i += jl_bt_entry_size(signal_bt_data + i)) { - jl_print_bt_entry_codeloc(signal_bt_data + i); + jl_print_bt_entry_codeloc(sig, signal_bt_data + i); } } } diff --git a/src/signals-win.c b/src/signals-win.c index c8ae74f52dba4..d9c7ffd5ae769 100644 --- a/src/signals-win.c +++ b/src/signals-win.c @@ -4,7 +4,7 @@ // Note that this file is `#include`d by "signal-handling.c" #include // hidden by LEAN_AND_MEAN -static const size_t sig_stack_size = 131072; // 128k reserved for backtrace_fiber for stack overflow handling +const size_t sig_stack_size = 131072; // 128k reserved for backtrace_fiber for stack overflow handling // Copied from MINGW_FLOAT_H which may not be found due to a collision with the builtin gcc float.h // eventually we can probably integrate this into OpenLibm. @@ -333,7 +333,7 @@ LONG WINAPI jl_exception_handler(struct _EXCEPTION_POINTERS *ExceptionInfo) jl_safe_printf("UNKNOWN"); break; } jl_safe_printf(" at 0x%zx -- ", (size_t)ExceptionInfo->ExceptionRecord->ExceptionAddress); - jl_print_native_codeloc((uintptr_t)ExceptionInfo->ExceptionRecord->ExceptionAddress); + jl_print_native_codeloc("", (uintptr_t)ExceptionInfo->ExceptionRecord->ExceptionAddress); jl_critical_error(0, 0, ExceptionInfo->ContextRecord, ct); static int recursion = 0; diff --git a/src/stackwalk.c b/src/stackwalk.c index 50566b46ff45a..b04142f7cf845 100644 --- a/src/stackwalk.c +++ b/src/stackwalk.c @@ -637,22 +637,25 @@ JL_DLLEXPORT jl_value_t *jl_lookup_code_address(void *ip, int skipC) return rs; } -static void jl_safe_print_codeloc(const char* func_name, const char* file_name, +static void jl_safe_print_codeloc(const char *pre_str, + const char* func_name, const char* file_name, int line, int inlined) JL_NOTSAFEPOINT { const char *inlined_str = inlined ? " [inlined]" : ""; if (line != -1) { - jl_safe_printf("%s at %s:%d%s\n", func_name, file_name, line, inlined_str); + jl_safe_printf("%s%s at %s:%d%s\n", + pre_str, func_name, file_name, line, inlined_str); } else { - jl_safe_printf("%s at %s (unknown line)%s\n", func_name, file_name, inlined_str); + jl_safe_printf("%s%s at %s (unknown line)%s\n", + pre_str, func_name, file_name, inlined_str); } } // Print function, file and line containing native instruction pointer `ip` by // looking up debug info. Prints multiple such frames when `ip` points to // inlined code. -void jl_print_native_codeloc(uintptr_t ip) JL_NOTSAFEPOINT +void jl_print_native_codeloc(char *pre_str, uintptr_t ip) JL_NOTSAFEPOINT { // This function is not allowed to reference any TLS variables since // it can be called from an unmanaged thread on OSX. @@ -664,10 +667,11 @@ void jl_print_native_codeloc(uintptr_t ip) JL_NOTSAFEPOINT for (i = 0; i < n; i++) { jl_frame_t frame = frames[i]; if (!frame.func_name) { - jl_safe_printf("unknown function (ip: %p) at %s\n", (void*)ip, frame.file_name ? frame.file_name : "(unknown file)"); + jl_safe_printf("%sunknown function (ip: %p) at %s\n", pre_str, (void*)ip, frame.file_name ? frame.file_name : "(unknown file)"); } else { - jl_safe_print_codeloc(frame.func_name, frame.file_name, frame.line, frame.inlined); + jl_safe_print_codeloc(pre_str, frame.func_name, + frame.file_name, frame.line, frame.inlined); free(frame.func_name); } free(frame.file_name); @@ -725,7 +729,7 @@ const char *jl_debuginfo_name(jl_value_t *func) // func == module : top-level // func == NULL : macro expansion -static void jl_print_debugloc(jl_debuginfo_t *debuginfo, jl_value_t *func, size_t ip, int inlined) JL_NOTSAFEPOINT +static void jl_print_debugloc(const char *pre_str, jl_debuginfo_t *debuginfo, jl_value_t *func, size_t ip, int inlined) JL_NOTSAFEPOINT { if (!jl_is_symbol(debuginfo->def)) // this is a path or func = debuginfo->def; // this is inlined code @@ -734,26 +738,36 @@ static void jl_print_debugloc(jl_debuginfo_t *debuginfo, jl_value_t *func, size_ if (edges_idx) { jl_debuginfo_t *edge = (jl_debuginfo_t*)jl_svecref(debuginfo->edges, edges_idx - 1); assert(jl_typetagis(edge, jl_debuginfo_type)); - jl_print_debugloc(edge, NULL, stmt.pc, 1); + jl_print_debugloc(pre_str, edge, NULL, stmt.pc, 1); } intptr_t ip2 = stmt.line; if (ip2 >= 0 && ip > 0 && (jl_value_t*)debuginfo->linetable != jl_nothing) { - jl_print_debugloc(debuginfo->linetable, func, ip2, 0); + jl_print_debugloc(pre_str, debuginfo->linetable, func, ip2, 0); } else { if (ip2 < 0) // set broken debug info to ignored ip2 = 0; const char *func_name = jl_debuginfo_name(func); const char *file = jl_debuginfo_file(debuginfo); - jl_safe_print_codeloc(func_name, file, ip2, inlined); + jl_safe_print_codeloc(pre_str, func_name, file, ip2, inlined); } } // Print code location for backtrace buffer entry at *bt_entry -void jl_print_bt_entry_codeloc(jl_bt_element_t *bt_entry) JL_NOTSAFEPOINT +void jl_print_bt_entry_codeloc(int sig, jl_bt_element_t *bt_entry) JL_NOTSAFEPOINT { + char sig_str[32], pre_str[64]; + sig_str[0] = pre_str[0] = '\0'; + if (sig != -1) { + snprintf(sig_str, 32, "signal (%d) ", sig); + } + // do not call jl_threadid if there's no current task + if (jl_get_current_task()) { + snprintf(pre_str, 64, "%sthread (%d) ", sig_str, jl_threadid() + 1); + } + if (jl_bt_is_native(bt_entry)) { - jl_print_native_codeloc(bt_entry[0].uintptr); + jl_print_native_codeloc(pre_str, bt_entry[0].uintptr); } else if (jl_bt_entry_tag(bt_entry) == JL_BT_INTERP_FRAME_TAG) { size_t ip = jl_bt_entry_header(bt_entry); // zero-indexed @@ -772,7 +786,7 @@ void jl_print_bt_entry_codeloc(jl_bt_element_t *bt_entry) JL_NOTSAFEPOINT if (jl_is_code_info(code)) { jl_code_info_t *src = (jl_code_info_t*)code; // See also the debug info handling in codegen.cpp. - jl_print_debugloc(src->debuginfo, def, ip + 1, 0); + jl_print_debugloc(pre_str, src->debuginfo, def, ip + 1, 0); } else { // If we're using this function something bad has already happened; @@ -1361,7 +1375,13 @@ JL_DLLEXPORT jl_record_backtrace_result_t jl_record_backtrace(jl_task_t *t, jl_b JL_DLLEXPORT void jl_gdblookup(void* ip) { - jl_print_native_codeloc((uintptr_t)ip); + char pre_str[64]; + pre_str[0] = '\0'; + // do not call jl_threadid if there's no current task + if (jl_get_current_task()) { + snprintf(pre_str, 64, "thread (%d) ", jl_threadid() + 1); + } + jl_print_native_codeloc(pre_str, (uintptr_t)ip); } // Print backtrace for current exception in catch block @@ -1376,7 +1396,7 @@ JL_DLLEXPORT void jlbacktrace(void) JL_NOTSAFEPOINT size_t i, bt_size = jl_excstack_bt_size(s, s->top); jl_bt_element_t *bt_data = jl_excstack_bt_data(s, s->top); for (i = 0; i < bt_size; i += jl_bt_entry_size(bt_data + i)) { - jl_print_bt_entry_codeloc(bt_data + i); + jl_print_bt_entry_codeloc(-1, bt_data + i); } } @@ -1399,7 +1419,7 @@ JL_DLLEXPORT void jlbacktracet(jl_task_t *t) JL_NOTSAFEPOINT size_t bt_size = r.bt_size; size_t i; for (i = 0; i < bt_size; i += jl_bt_entry_size(bt_data + i)) { - jl_print_bt_entry_codeloc(bt_data + i); + jl_print_bt_entry_codeloc(-1, bt_data + i); } if (bt_size == 0) jl_safe_printf(" no backtrace recorded\n"); @@ -1410,11 +1430,30 @@ JL_DLLEXPORT void jl_print_backtrace(void) JL_NOTSAFEPOINT jlbacktrace(); } -// Print backtraces for all live tasks, for all threads, to jl_safe_printf stderr +extern int jl_inside_heartbeat_thread(void); +extern int jl_heartbeat_pause(void); +extern int jl_heartbeat_resume(void); + +// Print backtraces for all live tasks, for all threads, to jl_safe_printf +// stderr. This can take a _long_ time! JL_DLLEXPORT void jl_print_task_backtraces(int show_done) JL_NOTSAFEPOINT { + // disable heartbeats to prevent heartbeat loss while running this, + // unless this is called from the heartbeat thread itself; in that + // situation, the thread is busy running this and it will not be + // updating the missed heartbeats counter + if (!jl_inside_heartbeat_thread()) { + jl_heartbeat_pause(); + } + size_t nthreads = jl_atomic_load_acquire(&jl_n_threads); jl_ptls_t *allstates = jl_atomic_load_relaxed(&jl_all_tls_states); + int ctid = -1; + // do not call jl_threadid if there's no current task + if (jl_get_current_task()) { + ctid = jl_threadid() + 1; + } + jl_safe_printf("thread (%d) ++++ Task backtraces\n", ctid); for (size_t i = 0; i < nthreads; i++) { jl_ptls_t ptls2 = allstates[i]; if (gc_is_collector_thread(i)) { @@ -1430,17 +1469,22 @@ JL_DLLEXPORT void jl_print_task_backtraces(int show_done) JL_NOTSAFEPOINT jl_task_t *t = ptls2->root_task; if (t != NULL) t_state = jl_atomic_load_relaxed(&t->_state); - jl_safe_printf("==== Thread %d created %zu live tasks\n", - ptls2->tid + 1, n + (t_state != JL_TASK_STATE_DONE)); + jl_safe_printf("thread (%d) ==== Thread %d created %zu live tasks\n", + ctid, ptls2->tid + 1, n + (t_state != JL_TASK_STATE_DONE)); if (show_done || t_state != JL_TASK_STATE_DONE) { - jl_safe_printf(" ---- Root task (%p)\n", ptls2->root_task); + jl_safe_printf("thread (%d) ---- Root task (%p)\n", ctid, ptls2->root_task); if (t != NULL) { - jl_safe_printf(" (sticky: %d, started: %d, state: %d, tid: %d)\n", - t->sticky, t->ctx.started, t_state, + jl_safe_printf("thread (%d) (sticky: %d, started: %d, state: %d, tid: %d)\n", + ctid, t->sticky, t->ctx.started, t_state, jl_atomic_load_relaxed(&t->tid) + 1); - jlbacktracet(t); + if (t->ctx.stkbuf != NULL) { + jlbacktracet(t); + } + else { + jl_safe_printf("thread (%d) no stack\n", ctid); + } } - jl_safe_printf(" ---- End root task\n"); + jl_safe_printf("thread (%d) ---- End root task\n", ctid); } for (size_t j = 0; j < n; j++) { @@ -1450,17 +1494,24 @@ JL_DLLEXPORT void jl_print_task_backtraces(int show_done) JL_NOTSAFEPOINT int t_state = jl_atomic_load_relaxed(&t->_state); if (!show_done && t_state == JL_TASK_STATE_DONE) continue; - jl_safe_printf(" ---- Task %zu (%p)\n", j + 1, t); + jl_safe_printf("thread (%d) ---- Task %zu (%p)\n", ctid, j + 1, t); // n.b. this information might not be consistent with the stack printing after it, since it could start running or change tid, etc. - jl_safe_printf(" (sticky: %d, started: %d, state: %d, tid: %d)\n", - t->sticky, t->ctx.started, t_state, + jl_safe_printf("thread (%d) (sticky: %d, started: %d, state: %d, tid: %d)\n", + ctid, t->sticky, t->ctx.started, t_state, jl_atomic_load_relaxed(&t->tid) + 1); - jlbacktracet(t); - jl_safe_printf(" ---- End task %zu\n", j + 1); + if (t->ctx.stkbuf != NULL) + jlbacktracet(t); + else + jl_safe_printf("thread (%d) no stack\n", ctid); + jl_safe_printf("thread (%d) ---- End task %zu\n", ctid, j + 1); } - jl_safe_printf("==== End thread %d\n", ptls2->tid + 1); + jl_safe_printf("thread (%d) ==== End thread %d\n", ctid, ptls2->tid + 1); + } + jl_safe_printf("thread (%d) ++++ Done\n", ctid); + + if (!jl_inside_heartbeat_thread()) { + jl_heartbeat_resume(); } - jl_safe_printf("==== Done\n"); } #ifdef __cplusplus 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) { 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/src/task.c b/src/task.c index 019a301b1f062..bb86bce48101a 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; @@ -1108,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 @@ -1573,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/src/threading.c b/src/threading.c index 9f5c18fe53555..e56aba2de51a1 100644 --- a/src/threading.c +++ b/src/threading.c @@ -1111,6 +1111,289 @@ JL_DLLEXPORT int jl_setaffinity(int16_t tid, char *mask, int cpumasksize) { return 0; // success } +// Heartbeat mechanism for Julia's task scheduler +// --- +// Start a thread that does not participate in running Julia's tasks. This +// thread simply sleeps until the heartbeat mechanism is enabled. When +// enabled, the heartbeat thread enters a loop in which it blocks waiting +// for the specified heartbeat interval. If, within that interval, +// `jl_heartbeat()` is *not* called at least once, then the thread calls +// `jl_print_task_backtraces(0)`. + +#ifdef JL_HEARTBEAT_THREAD + +#include + +volatile int heartbeat_enabled; +int heartbeat_tid; // Mostly used to ensure we skip this thread in the CPU profiler. XXX: not implemented on Windows +uv_thread_t heartbeat_uvtid; +uv_sem_t heartbeat_on_sem, // jl_heartbeat_enable -> thread + heartbeat_off_sem; // thread -> jl_heartbeat_enable +int heartbeat_interval_s, + tasks_after_n, + reset_tasks_after_n; +int tasks_showed, n_hbs_missed, n_hbs_recvd; +_Atomic(int) heartbeats; + +JL_DLLEXPORT void jl_print_task_backtraces(int show_done) JL_NOTSAFEPOINT; +void jl_heartbeat_threadfun(void *arg); + +// start the heartbeat thread with heartbeats disabled +void jl_init_heartbeat(void) +{ + heartbeat_enabled = 0; + uv_sem_init(&heartbeat_on_sem, 0); + uv_sem_init(&heartbeat_off_sem, 0); + uv_thread_create(&heartbeat_uvtid, jl_heartbeat_threadfun, NULL); + uv_thread_detach(&heartbeat_uvtid); +} + +int jl_inside_heartbeat_thread(void) +{ + uv_thread_t curr_uvtid = uv_thread_self(); + return curr_uvtid == heartbeat_uvtid; +} + +// enable/disable heartbeats +// heartbeat_s: interval within which jl_heartbeat() must be called +// show_tasks_after_n: number of heartbeats missed before printing task backtraces +// reset_after_n: number of heartbeats after which to reset +// +// When disabling heartbeats, the heartbeat thread must wake up, +// find out that heartbeats are now disabled, and reset. For now, we +// handle this by preventing re-enabling of heartbeats until this +// completes. +JL_DLLEXPORT int jl_heartbeat_enable(int heartbeat_s, int show_tasks_after_n, + int reset_after_n) +{ + if (heartbeat_s <= 0) { + heartbeat_enabled = 0; + heartbeat_interval_s = tasks_after_n = reset_tasks_after_n = 0; + } + else { + // must disable before enabling + if (heartbeat_enabled) { + return -1; + } + // heartbeat thread must be ready + if (uv_sem_trywait(&heartbeat_off_sem) != 0) { + return -1; + } + + jl_atomic_store_relaxed(&heartbeats, 0); + heartbeat_interval_s = heartbeat_s; + tasks_after_n = show_tasks_after_n; + reset_tasks_after_n = reset_after_n; + tasks_showed = 0; + n_hbs_missed = 0; + n_hbs_recvd = 0; + heartbeat_enabled = 1; + uv_sem_post(&heartbeat_on_sem); // wake the heartbeat thread + } + return 0; +} + +// temporarily pause the heartbeat thread +JL_DLLEXPORT int jl_heartbeat_pause(void) +{ + if (!heartbeat_enabled) { + return -1; + } + heartbeat_enabled = 0; + return 0; +} + +// resume the paused heartbeat thread +JL_DLLEXPORT int jl_heartbeat_resume(void) +{ + // cannot resume if the heartbeat thread is already running + if (heartbeat_enabled) { + return -1; + } + + // cannot resume if we weren't paused (disabled != paused) + if (heartbeat_interval_s == 0) { + return -1; + } + + // heartbeat thread must be ready + if (uv_sem_trywait(&heartbeat_off_sem) != 0) { + return -1; + } + + // reset state as we've been paused + n_hbs_missed = 0; + n_hbs_recvd = 0; + tasks_showed = 0; + + // resume + heartbeat_enabled = 1; + uv_sem_post(&heartbeat_on_sem); // wake the heartbeat thread + return 0; +} + +// heartbeat +JL_DLLEXPORT void jl_heartbeat(void) +{ + jl_atomic_fetch_add(&heartbeats, 1); +} + +// sleep the thread for the specified interval +void sleep_for(int secs, int nsecs) +{ + struct timespec rqtp, rmtp; + rqtp.tv_sec = secs; + rqtp.tv_nsec = nsecs; + rmtp.tv_sec = 0; + rmtp.tv_nsec = 0; + for (; ;) { + // this suspends the thread so we aren't using CPU + if (nanosleep(&rqtp, &rmtp) == 0) { + return; + } + // TODO: else if (errno == EINTR) + // this could be SIGTERM and we should shutdown but how to find out? + rqtp = rmtp; + } +} + +// check for heartbeats and maybe report loss +uint8_t check_heartbeats(uint8_t gc_state) +{ + int hb = jl_atomic_exchange(&heartbeats, 0); + + if (hb <= 0) { + // we didn't get a heartbeat + n_hbs_recvd = 0; + n_hbs_missed++; + + // if we've printed task backtraces already, do nothing + if (!tasks_showed) { + // otherwise, at least show this message + jl_safe_printf("==== heartbeat loss (%ds) ====\n", + n_hbs_missed * heartbeat_interval_s); + // if we've missed enough heartbeats, print task backtraces + if (n_hbs_missed >= tasks_after_n) { + jl_task_t *ct = jl_current_task; + jl_ptls_t ptls = ct->ptls; + + // exit GC-safe region to report then re-enter + jl_gc_safe_leave(ptls, gc_state); + jl_print_task_backtraces(0); + gc_state = jl_gc_safe_enter(ptls); + + // we printed task backtraces + tasks_showed = 1; + } + } + } + else { + // got a heartbeat + n_hbs_recvd++; + // if we'd printed task backtraces, check for reset + if (tasks_showed && n_hbs_recvd >= reset_tasks_after_n) { + tasks_showed = 0; + jl_safe_printf("==== heartbeats recovered (lost for %ds) ====\n", + n_hbs_missed * heartbeat_interval_s); + } + n_hbs_missed = 0; + } + + return gc_state; +} + +// heartbeat thread function +void jl_heartbeat_threadfun(void *arg) +{ + int s = 59, ns = 1e9 - 1, rs; + uint64_t t0, tchb; + + // We need a TLS because backtraces are accumulated into ptls->bt_size + // and ptls->bt_data, so we need to call jl_adopt_thread(). + jl_adopt_thread(); + (void)jl_atomic_fetch_add_relaxed(&n_threads_running, -1); + jl_task_t *ct = jl_current_task; + jl_ptls_t ptls = ct->ptls; + heartbeat_tid = ptls->tid; + + // Don't hold up GC, this thread doesn't participate. + uint8_t gc_state = jl_gc_safe_enter(ptls); + + for (;;) { + if (!heartbeat_enabled) { + // post the off semaphore to indicate we're ready to enable + uv_sem_post(&heartbeat_off_sem); + + // sleep the thread here; this semaphore is posted in + // jl_heartbeat_enable() or jl_heartbeat_resume() + uv_sem_wait(&heartbeat_on_sem); + + // Set the sleep duration. + s = heartbeat_interval_s - 1; + ns = 1e9 - 1; + continue; + } + + // heartbeat is enabled; sleep, waiting for the desired interval + sleep_for(s, ns); + + // if heartbeats were turned off/paused while we were sleeping, reset + if (!heartbeat_enabled) { + continue; + } + + // check if any heartbeats have happened, report as appropriate + t0 = jl_hrtime(); + gc_state = check_heartbeats(gc_state); + tchb = jl_hrtime() - t0; + + // adjust the next sleep duration based on how long the heartbeat + // check took, but if it took too long then use the normal duration + rs = 1; + while (tchb > 1e9) { + rs++; + tchb -= 1e9; + } + if (rs < heartbeat_interval_s) { + s = heartbeat_interval_s - rs; + } + ns = 1e9 - tchb; + } +} + +#else // !JL_HEARTBEAT_THREAD + +void jl_init_heartbeat(void) +{ +} + +int jl_inside_heartbeat_thread(void) +{ + return 0; +} + +JL_DLLEXPORT int jl_heartbeat_enable(int heartbeat_s, int show_tasks_after_n, + int reset_after_n) +{ + return -1; +} + +JL_DLLEXPORT int jl_heartbeat_pause(void) +{ + return -1; +} + +JL_DLLEXPORT int jl_heartbeat_resume(void) +{ + return -1; +} + +JL_DLLEXPORT void jl_heartbeat(void) +{ +} + +#endif // JL_HEARTBEAT_THREAD + #ifdef __cplusplus } #endif diff --git a/stdlib/Artifacts/src/Artifacts.jl b/stdlib/Artifacts/src/Artifacts.jl index 4af706606d326..79cb05e86e354 100644 --- a/stdlib/Artifacts/src/Artifacts.jl +++ b/stdlib/Artifacts/src/Artifacts.jl @@ -16,6 +16,13 @@ using Base.TOML: TOML export artifact_exists, artifact_path, artifact_meta, artifact_hash, select_downloadable_artifacts, find_artifacts_toml, @artifact_str +const _artifacts_world_age = Ref{UInt}(typemax(UInt)) + +function __init__() + _artifacts_world_age[] = Base.get_world_counter() + nothing +end + """ parse_toml(path::String) @@ -403,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 @@ -543,7 +550,7 @@ function jointail(dir, tail) end function _artifact_str(__module__, artifacts_toml, name, path_tail, artifact_dict, hash, platform, ::Val{LazyArtifacts}) where LazyArtifacts - world = Base._require_world_age[] + world = _artifacts_world_age[] if world == typemax(UInt) world = Base.get_world_counter() end diff --git a/stdlib/LinearAlgebra.version b/stdlib/LinearAlgebra.version index 3b1f7cc5847d9..6caafbaf313ac 100644 --- a/stdlib/LinearAlgebra.version +++ b/stdlib/LinearAlgebra.version @@ -1,4 +1,4 @@ LINEARALGEBRA_BRANCH = release-1.12 -LINEARALGEBRA_SHA1 = 997c4b7e7664f30645ef8bd51d8a4203e49c4631 +LINEARALGEBRA_SHA1 = 7249e662d66b53da6c9335ea748eac2750c2924f LINEARALGEBRA_GIT_URL := https://github.com/JuliaLang/LinearAlgebra.jl.git LINEARALGEBRA_TAR_URL = https://api.github.com/repos/JuliaLang/LinearAlgebra.jl/tarball/$1 diff --git a/stdlib/Logging/test/runtests.jl b/stdlib/Logging/test/runtests.jl index 3e92b7d9e2697..a0ba9f7c43b1e 100644 --- a/stdlib/Logging/test/runtests.jl +++ b/stdlib/Logging/test/runtests.jl @@ -19,6 +19,13 @@ macro customlog(exs...) Base.CoreLogging.logmsg_code((Base.CoreLogging.@_sourcei @test :handle_message in names(Logging, all=true) # non-exported public function end +@testset "LogLevel compatibility with integers" begin + @test Logging.Debug + 1000 == Logging.Info + @test Logging.Warn - 1000 == Logging.Info + @test Logging.Info < 500 + @test 500 < Logging.Warn +end + @testset "ConsoleLogger" begin # First pass log limiting @test min_enabled_level(ConsoleLogger(devnull, Logging.Debug)) == Logging.Debug diff --git a/stdlib/Manifest.toml b/stdlib/Manifest.toml index 3db51cf75328d..14482fd3001b6 100644 --- a/stdlib/Manifest.toml +++ b/stdlib/Manifest.toml @@ -240,6 +240,11 @@ weakdeps = ["SparseArrays"] uuid = "f489334b-da3d-4c2e-b8f0-e476e12c162b" version = "1.11.0" +[[deps.SuiteSparse]] +deps = ["Libdl", "LinearAlgebra", "Serialization", "SparseArrays"] +uuid = "4607b0f0-06f3-5cda-b6b1-a6196a1729e9" +version = "1.12.0" + [[deps.SuiteSparse_jll]] deps = ["Artifacts", "Libdl", "libblastrampoline_jll"] uuid = "bea87d4a-7f5b-5778-9afe-8cc45184846c" diff --git a/stdlib/MozillaCACerts_jll/Project.toml b/stdlib/MozillaCACerts_jll/Project.toml index 57c5526a6f1f2..d63251d59d58f 100644 --- a/stdlib/MozillaCACerts_jll/Project.toml +++ b/stdlib/MozillaCACerts_jll/Project.toml @@ -1,7 +1,7 @@ name = "MozillaCACerts_jll" uuid = "14a3606d-f60d-562e-9121-12d972cd8159" # Keep in sync with `deps/libgit2.version`. -version = "2025.05.20" +version = "2025.11.04" [extras] Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" 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 diff --git a/stdlib/Pkg.version b/stdlib/Pkg.version index 6feeb870e2f20..56f673f582df6 100644 --- a/stdlib/Pkg.version +++ b/stdlib/Pkg.version @@ -1,4 +1,4 @@ PKG_BRANCH = release-1.12 -PKG_SHA1 = 53b2b5da91c27515ce129635fe184e8bd9afb09f +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 diff --git a/stdlib/Project.toml b/stdlib/Project.toml index 1e03a0f474490..8a3e0f4d78e69 100644 --- a/stdlib/Project.toml +++ b/stdlib/Project.toml @@ -48,6 +48,7 @@ SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" StyledStrings = "f489334b-da3d-4c2e-b8f0-e476e12c162b" SuiteSparse_jll = "bea87d4a-7f5b-5778-9afe-8cc45184846c" +SuiteSparse = "4607b0f0-06f3-5cda-b6b1-a6196a1729e9" TOML = "fa267f1f-6049-4f14-aa54-33bafae1ed76" Tar = "a4e569a6-e804-4fa4-b0f3-eef7a1d5b13e" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" 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/src/precompile.jl b/stdlib/REPL/src/precompile.jl index a4d289db8cd40..21ca166b94a71 100644 --- a/stdlib/REPL/src/precompile.jl +++ b/stdlib/REPL/src/precompile.jl @@ -61,7 +61,7 @@ function repl_workload() display("a string") foo(x) = 1 @time @eval foo(1) - ; pwd + ; $CTRL_C $CTRL_R$CTRL_C# ? reinterpret 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 diff --git a/stdlib/SparseArrays.version b/stdlib/SparseArrays.version index b6d9a820d9a06..0e282a70c0acb 100644 --- a/stdlib/SparseArrays.version +++ b/stdlib/SparseArrays.version @@ -1,4 +1,4 @@ SPARSEARRAYS_BRANCH = release-1.12 -SPARSEARRAYS_SHA1 = 5d674dc7bd90156cf8ecea4e143b69b5a5b7640d +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 diff --git a/stdlib/stdlib.mk b/stdlib/stdlib.mk index 006b7a276a3b3..1c418a7e1a8f4 100644 --- a/stdlib/stdlib.mk +++ b/stdlib/stdlib.mk @@ -6,8 +6,8 @@ INDEPENDENT_STDLIBS := \ ArgTools Base64 CRC32c Dates DelimitedFiles Distributed Downloads Future \ InteractiveUtils JuliaSyntaxHighlighting LazyArtifacts LibGit2 LibCURL Logging \ Markdown Mmap NetworkOptions Profile Printf Pkg REPL Serialization SharedArrays \ - SparseArrays Statistics StyledStrings SuiteSparse_jll Tar Test TOML Unicode UUIDs \ - dSFMT_jll GMP_jll libLLVM_jll LLD_jll LLVMLibUnwind_jll LibUnwind_jll LibUV_jll \ + SparseArrays Statistics StyledStrings SuiteSparse_jll SuiteSparse Tar Test TOML \ + Unicode UUIDs dSFMT_jll GMP_jll libLLVM_jll LLD_jll LLVMLibUnwind_jll LibUnwind_jll LibUV_jll \ LibCURL_jll LibSSH2_jll LibGit2_jll nghttp2_jll MozillaCACerts_jll \ MPFR_jll OpenLibm_jll OpenSSL_jll PCRE2_jll p7zip_jll Zlib_jll diff --git a/test/core.jl b/test/core.jl index d18f7fe8136b6..dd2dfd86b9619 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() @@ -8605,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 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 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)) 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) 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 diff --git a/test/scopedvalues.jl b/test/scopedvalues.jl index e9b36d80fc2c4..500b9647f8da6 100644 --- a/test/scopedvalues.jl +++ b/test/scopedvalues.jl @@ -195,3 +195,74 @@ nothrow_scope() push!(ts, 2) end end + +# LazyScopedValue +global lsv_ncalled = 0 +const lsv = LazyScopedValue{Int}(OncePerProcess(() -> (global lsv_ncalled; lsv_ncalled += 1; 1))) +@testset "LazyScopedValue" begin + @test (@with lsv=>2 lsv[]) == 2 + @test lsv_ncalled == 0 + @test lsv[] == 1 + @test lsv_ncalled == 1 + @test lsv[] == 1 + @test lsv_ncalled == 1 +end + +@testset "ScopedThunk" begin + function check_svals() + @test sval[] == 8 + @test sval_float[] == 8.0 + end + sf = nothing + @with sval=>8 sval_float=>8.0 begin + sf = ScopedThunk(check_svals) + end + sf() + @with sval=>8 sval_float=>8.0 begin + sf2 = ScopedThunk{Function}(check_svals) + end + sf2() +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 diff --git a/test/syntax.jl b/test/syntax.jl index 70640e6c93d67..b2d7ebfa96f98 100644 --- a/test/syntax.jl +++ b/test/syntax.jl @@ -3659,7 +3659,7 @@ end @test p("public() = 6") == Expr(:(=), Expr(:call, :public), Expr(:block, 6)) end -@testset "removing argument sideeffects" begin +@testset "removing argument side effects" begin # Allow let blocks in broadcasted LHSes, but only evaluate them once: execs = 0 array = [1] @@ -3675,6 +3675,15 @@ end let; execs += 1; array; end::Vector{Int} .= 7 @test array == [7] @test execs == 4 + + # remove argument side effects on lhs kwcall + pa_execs = 0 + kw_execs = 0 + f60152(v, pa; kw) = copy(v) + @test (f60152([1, 2, 3], 0; kw=0) .*= 2) == [2,4,6] + @test (f60152([1, 2, 3], (pa_execs+=1); kw=(kw_execs+=1)) .*= 2) == [2,4,6] + @test pa_execs === 1 + @test kw_execs === 1 end # Allow GlobalRefs in macro definition 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