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/Makefile b/Makefile index b2d54d0a7a0e5..fd84ad0e7b033 100644 --- a/Makefile +++ b/Makefile @@ -26,7 +26,7 @@ default: $(JULIA_BUILD_MODE) # contains either "debug" or "release" all: debug release # sort is used to remove potential duplicates -DIRS := $(sort $(build_bindir) $(build_depsbindir) $(build_libdir) $(build_private_libdir) $(build_libexecdir) $(build_includedir) $(build_includedir)/julia $(build_sysconfdir)/julia $(build_datarootdir)/julia $(build_datarootdir)/julia/stdlib $(build_man1dir)) +DIRS := $(sort $(build_bindir) $(build_depsbindir) $(build_libdir) $(build_private_libdir) $(build_private_libexecdir) $(build_libexecdir) $(build_includedir) $(build_includedir)/julia $(build_sysconfdir)/julia $(build_datarootdir)/julia $(build_datarootdir)/julia/stdlib $(build_man1dir)) ifneq ($(BUILDROOT),$(JULIAHOME)) BUILDDIRS := $(BUILDROOT) $(addprefix $(BUILDROOT)/,base src src/flisp src/support src/clangsa cli doc deps stdlib test test/clangsa test/embedding test/gcext test/llvmpasses) BUILDDIRMAKE := $(addsuffix /Makefile,$(BUILDDIRS)) $(BUILDROOT)/sysimage.mk $(BUILDROOT)/pkgimage.mk 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/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 7c2d8ebe86de8..cece850ea265b 100644 --- a/base/file.jl +++ b/base/file.jl @@ -1059,7 +1059,7 @@ struct DirEntry end function Base.getproperty(obj::DirEntry, p::Symbol) if p === :path - return joinpath(obj.dir, obj.name) + return joinpath(getfield(obj, :dir), getfield(obj, :name)) else return getfield(obj, p) end 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/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/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/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/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-b322a8ff786472d680a972e57575b4586fefe018.tar.gz/md5 b/deps/checksums/Pkg-b322a8ff786472d680a972e57575b4586fefe018.tar.gz/md5 deleted file mode 100644 index fbc81b025a8f9..0000000000000 --- a/deps/checksums/Pkg-b322a8ff786472d680a972e57575b4586fefe018.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -9fbd2f2f522bf0b5fd0d6b40f722db49 diff --git a/deps/checksums/Pkg-b322a8ff786472d680a972e57575b4586fefe018.tar.gz/sha512 b/deps/checksums/Pkg-b322a8ff786472d680a972e57575b4586fefe018.tar.gz/sha512 deleted file mode 100644 index 576ad3c62915e..0000000000000 --- a/deps/checksums/Pkg-b322a8ff786472d680a972e57575b4586fefe018.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -77e2f3729e58b74cfdd1fc2d991fc6552e7f7ceb7b9fdc837e5885932e2f15d6ab3c76b6ab90b1a6c66050dab7279abc0b976ee52b840b757dd13b1a955f5c5c diff --git a/deps/checksums/SparseArrays-2376bf8734754c5dd4264186299ae6839ab7783f.tar.gz/md5 b/deps/checksums/SparseArrays-2376bf8734754c5dd4264186299ae6839ab7783f.tar.gz/md5 new file mode 100644 index 0000000000000..233004ebc84fe --- /dev/null +++ b/deps/checksums/SparseArrays-2376bf8734754c5dd4264186299ae6839ab7783f.tar.gz/md5 @@ -0,0 +1 @@ +059aef1395d4160561236167379af7ae diff --git a/deps/checksums/SparseArrays-2376bf8734754c5dd4264186299ae6839ab7783f.tar.gz/sha512 b/deps/checksums/SparseArrays-2376bf8734754c5dd4264186299ae6839ab7783f.tar.gz/sha512 new file mode 100644 index 0000000000000..ea773676bed87 --- /dev/null +++ b/deps/checksums/SparseArrays-2376bf8734754c5dd4264186299ae6839ab7783f.tar.gz/sha512 @@ -0,0 +1 @@ +69015dbc54d0d374b89eea2464eafd0aae461ad819965ad5745fc5ce50cf9519eb4d4024209184a90f2704873a96a0fd3586bf354a036c4c44452141d5535f43 diff --git a/deps/checksums/SparseArrays-f81a30d962b03d4048b26439d60979673e343b67.tar.gz/md5 b/deps/checksums/SparseArrays-f81a30d962b03d4048b26439d60979673e343b67.tar.gz/md5 deleted file mode 100644 index 372c4735d1c07..0000000000000 --- a/deps/checksums/SparseArrays-f81a30d962b03d4048b26439d60979673e343b67.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -230a4d9544d2cec7c4adf9a50d228345 diff --git a/deps/checksums/SparseArrays-f81a30d962b03d4048b26439d60979673e343b67.tar.gz/sha512 b/deps/checksums/SparseArrays-f81a30d962b03d4048b26439d60979673e343b67.tar.gz/sha512 deleted file mode 100644 index 6f1838c5bb750..0000000000000 --- a/deps/checksums/SparseArrays-f81a30d962b03d4048b26439d60979673e343b67.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -d66a3b6e032af45b78f71d8c5851c3e6fe1c5159f4c4428b64f1f6a52a111a488a9cec8f702a9e5de96593f5d4735f7490ae6c33eee7155b31f743b82aebd38b diff --git a/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/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/gc-interface.h b/src/gc-interface.h index 826e91355b17a..c67b7720e70a5 100644 --- a/src/gc-interface.h +++ b/src/gc-interface.h @@ -241,6 +241,13 @@ STATIC_INLINE void jl_gc_wb(const void *parent, const void *ptr) JL_NOTSAFEPOINT // can be used to annotate that a write barrier would be required were it not for this property // (as opposed to somebody just having forgotten to think about write barriers). STATIC_INLINE void jl_gc_wb_fresh(const void *parent, const void *ptr) JL_NOTSAFEPOINT {} +// As an optimization, the current_task is explicitly added to the remset while it is running. +// Upon deschedule, we conservatively move the write barrier into the young generation. +// This allows the omission of write barriers for all GC roots on the current task stack (JL_GC_PUSH_*), +// as well as the Task's explicit fields (but only for the current task). +// This function is a no-op that can be used to annotate that a write barrier would be required were +// it not for this property (as opposed to somebody just having forgotten to think about write barriers). +STATIC_INLINE void jl_gc_wb_current_task(const void *parent JL_UNUSED, const void *ptr JL_UNUSED) JL_NOTSAFEPOINT {} // Used to annotate that a write barrier would be required, but may be omitted because `ptr` // is known to be an old object. STATIC_INLINE void jl_gc_wb_knownold(const void *parent, const void *ptr) JL_NOTSAFEPOINT {} diff --git a/src/gc-stock.c b/src/gc-stock.c index 55f31b26679ff..a95dc5f731a5b 100644 --- a/src/gc-stock.c +++ b/src/gc-stock.c @@ -197,6 +197,7 @@ static int mark_reset_age = 0; static int64_t scanned_bytes; // young bytes scanned while marking static int64_t perm_scanned_bytes; // old bytes scanned while marking +static int64_t heap_size_after_last_full_gc = 0; int prev_sweep_full = 1; int current_sweep_full = 0; int next_sweep_full = 0; @@ -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; } diff --git a/src/gc-stock.h b/src/gc-stock.h index d478ee1366da0..3abd42ea1bfd3 100644 --- a/src/gc-stock.h +++ b/src/gc-stock.h @@ -483,7 +483,8 @@ FORCE_INLINE void gc_big_object_link(bigval_t *sentinel_node, bigval_t *node) JL #define FULL_SWEEP_REASON_FORCED_FULL_SWEEP (1) #define FULL_SWEEP_REASON_USER_MAX_EXCEEDED (2) #define FULL_SWEEP_REASON_LARGE_PROMOTION_RATE (3) -#define FULL_SWEEP_NUM_REASONS (4) +#define FULL_SWEEP_REASON_LARGE_HEAP_GROWTH (4) +#define FULL_SWEEP_NUM_REASONS (5) extern JL_DLLEXPORT uint64_t jl_full_sweep_reasons[FULL_SWEEP_NUM_REASONS]; STATIC_INLINE void gc_count_full_sweep_reason(int reason) JL_NOTSAFEPOINT 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..2c5abf7343d09 100644 --- a/src/gf.c +++ b/src/gf.c @@ -399,6 +399,8 @@ static jl_code_instance_t *jl_method_inferred_with_abi(jl_method_instance_t *mi return NULL; } +jl_mutex_t jl_typeinf_lock; + // run type inference on lambda "mi" for given argument types. // returns the inferred source, and may cache the result in mi // if successful, also updates the mi argument to describe the validity of this src @@ -427,6 +429,7 @@ jl_code_instance_t *jl_type_infer(jl_method_instance_t *mi, size_t world, uint8_ return NULL; JL_TIMING(INFERENCE, INFERENCE); jl_value_t **fargs; + JL_GC_PUSH1(&ci); JL_GC_PUSHARGS(fargs, 5); fargs[0] = (jl_value_t*)jl_typeinf_func; fargs[1] = (jl_value_t*)mi; @@ -458,6 +461,7 @@ jl_code_instance_t *jl_type_infer(jl_method_instance_t *mi, size_t world, uint8_ // increase that limit, we'll need to // allocate another bit for the counter. ct->reentrant_timing += 0b10; + JL_LOCK(&jl_typeinf_lock); JL_TRY { ci = (jl_code_instance_t*)jl_apply(fargs, 5); } @@ -481,6 +485,7 @@ jl_code_instance_t *jl_type_infer(jl_method_instance_t *mi, size_t world, uint8_ abort(); #endif } + JL_UNLOCK(&jl_typeinf_lock); ct->world_age = last_age; ct->reentrant_timing -= 0b10; ct->ptls->in_pure_callback = last_pure; @@ -495,12 +500,11 @@ jl_code_instance_t *jl_type_infer(jl_method_instance_t *mi, size_t world, uint8_ // Record inference entrance backtrace if enabled if (ci) { - JL_GC_PUSH1(&ci); jl_push_inference_entrance_backtraces((jl_value_t*)ci); - JL_GC_POP(); } JL_GC_POP(); + JL_GC_POP(); #endif return ci; @@ -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: @@ -4169,7 +4173,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..f86435b3b9035 100644 --- a/src/init.c +++ b/src/init.c @@ -552,6 +552,7 @@ extern jl_mutex_t precomp_statement_out_lock; extern jl_mutex_t newly_inferred_mutex; extern jl_mutex_t global_roots_lock; extern jl_mutex_t profile_show_peek_cond_lock; +extern jl_mutex_t jl_typeinf_lock; static void restore_fp_env(void) { @@ -661,6 +662,7 @@ static void init_global_mutexes(void) { JL_MUTEX_INIT(&global_roots_lock, "global_roots_lock"); JL_MUTEX_INIT(&typecache_lock, "typecache_lock"); JL_MUTEX_INIT(&profile_show_peek_cond_lock, "profile_show_peek_cond_lock"); + JL_MUTEX_INIT(&jl_typeinf_lock, "jl_typeinf_lock"); } JL_DLLEXPORT void jl_init_(jl_image_buf_t sysimage) 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..4f2b5b7cfdd8f 100644 --- a/src/jl_exported_funcs.inc +++ b/src/jl_exported_funcs.inc @@ -523,8 +523,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 +544,7 @@ YY(jl_dump_fptr_asm) \ YY(jl_emit_native) \ YY(jl_get_function_id) \ + YY(jl_struct_to_llvm) \ YY(jl_type_to_llvm) \ YY(jl_getUnwindInfo) \ YY(jl_get_libllvm) \ diff --git a/src/julia_internal.h b/src/julia_internal.h index c9e1b0e204df6..0d3d45f35501f 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -2057,6 +2057,7 @@ JL_DLLIMPORT void jl_dump_native(void *native_code, const char *bc_fname, const char *unopt_bc_fname, const char *obj_fname, const char *asm_fname, ios_t *z, ios_t *s, jl_emission_params_t *params); JL_DLLIMPORT void jl_get_llvm_gvs(void *native_code, size_t *num_els, void **gvs); +JL_DLLIMPORT void jl_get_llvm_gvs_globals(void *native_code, size_t *num_els, void **gvs); JL_DLLIMPORT void jl_get_llvm_external_fns(void *native_code, size_t *num_els, jl_code_instance_t *gvs); JL_DLLIMPORT void jl_get_function_id(void *native_code, jl_code_instance_t *ncode, @@ -2064,7 +2065,8 @@ JL_DLLIMPORT void jl_get_function_id(void *native_code, jl_code_instance_t *ncod JL_DLLIMPORT void jl_register_fptrs(uint64_t image_base, const struct _jl_image_fptrs_t *fptrs, jl_method_instance_t **linfos, size_t n); JL_DLLIMPORT void jl_get_llvm_mis(void *native_code, size_t *num_els, - jl_method_instance_t *MIs); + jl_method_instance_t **MIs); +JL_DLLIMPORT void jl_get_llvm_cis(void *native_code, size_t *num_els, jl_code_instance_t **data); JL_DLLIMPORT void jl_init_codegen(void); JL_DLLIMPORT void jl_teardown_codegen(void) JL_NOTSAFEPOINT; JL_DLLIMPORT int jl_getFunctionInfo(jl_frame_t **frames, uintptr_t pointer, int skipC, int noInline) JL_NOTSAFEPOINT; diff --git a/src/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/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/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/stdlib/Artifacts/src/Artifacts.jl b/stdlib/Artifacts/src/Artifacts.jl index 0227d9532a49c..79cb05e86e354 100644 --- a/stdlib/Artifacts/src/Artifacts.jl +++ b/stdlib/Artifacts/src/Artifacts.jl @@ -410,7 +410,7 @@ function artifact_meta(name::String, artifact_dict::Dict, artifacts_toml::String dl_dict = Dict{Platform,Dict{String,Any}}() for x in meta x = x::Dict{String, Any} - dl_dict[unpack_platform(x, name, artifacts_toml)] = x + dl_dict[unpack_platform(x, name, artifacts_toml)::Platform] = x end meta = select_platform(dl_dict, platform) # If it's NOT a dict, complain 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 853940e299a8e..56f673f582df6 100644 --- a/stdlib/Pkg.version +++ b/stdlib/Pkg.version @@ -1,4 +1,4 @@ PKG_BRANCH = release-1.12 -PKG_SHA1 = b322a8ff786472d680a972e57575b4586fefe018 +PKG_SHA1 = 1c1c173d35be3eb478054fb0229b0b1b6aa040cf PKG_GIT_URL := https://github.com/JuliaLang/Pkg.jl.git PKG_TAR_URL = https://api.github.com/repos/JuliaLang/Pkg.jl/tarball/$1 diff --git a/stdlib/REPL/src/REPLCompletions.jl b/stdlib/REPL/src/REPLCompletions.jl index cd13f0880d7c4..0a5975179e3a3 100644 --- a/stdlib/REPL/src/REPLCompletions.jl +++ b/stdlib/REPL/src/REPLCompletions.jl @@ -1025,8 +1025,8 @@ function completions(string::String, pos::Int, context_module::Module=Main, shif if obj !== nothing # Skip leading whitespace inside brackets. i = @something findnext(!isspace, string, first(key)) nextind(string, last(key)) - key = i:last(key) - s = string[intersect(key, 1:pos)] + key = intersect(i:last(key), 1:pos) + s = string[key] matches = find_dict_matches(obj, s) length(matches) == 1 && !closed && (matches[1] *= ']') if length(matches) > 0 @@ -1051,7 +1051,8 @@ function completions(string::String, pos::Int, context_module::Module=Main, shif # "~/example.txt TAB => "/home/user/example.txt" r, closed = find_str(cur) if r !== nothing - s = do_string_unescape(string[intersect(r, 1:pos)]) + r = intersect(r, 1:pos) + s = do_string_unescape(string[r]) ret, success = complete_path_string(s, hint; string_escape=true, dirsep=Sys.iswindows() ? '\\' : '/') if length(ret) == 1 && !closed && close_path_completion(ret[1].path) @@ -1094,8 +1095,8 @@ function completions(string::String, pos::Int, context_module::Module=Main, shif # Keyword argument completion: # foo(ar TAB => keyword arguments like `arg1=` elseif kind(cur) == K"Identifier" - r = char_range(cur) - s = string[intersect(r, 1:pos)] + r = intersect(char_range(cur), 1:pos) + s = string[r] # Return without adding more suggestions if kwargs only complete_keyword_argument!(suggestions, e, s, context_module, arg_pos; shift) && return sort_suggestions(), r, true diff --git a/stdlib/REPL/test/replcompletions.jl b/stdlib/REPL/test/replcompletions.jl index de723cf83d0e7..428a13dd59e79 100644 --- a/stdlib/REPL/test/replcompletions.jl +++ b/stdlib/REPL/test/replcompletions.jl @@ -1492,6 +1492,23 @@ mktempdir() do path @test "$(path_expected)$(sep)foo_dir$(sep)" in c @test "$(path_expected)$(sep)foo_file.txt" in c end + + # Issue #60444: path completion should not delete text after the string (e.g. indexing) + let (c, r, res) = test_complete_pos("f(\"$(path)$(sep)foo|\")") + @test res + @test length(c) == 2 + @test "$(path_expected)$(sep)foo_dir$(sep)" in c + @test "$(path_expected)$(sep)foo_file.txt" in c + end + let (c, r, res) = test_complete_pos("f(\"$(path)$(sep)foo|\")[1]") + @test res + @test length(c) == 2 + @test "$(path_expected)$(sep)foo_dir$(sep)" in c + @test "$(path_expected)$(sep)foo_file.txt" in c + # Range should end at cursor position, not overwrite ")[1]" + pos = findfirst('|', "f(\"$(path)$(sep)foo|\")[1]") - 1 + @test last(r) == pos + end end if Sys.iswindows() @@ -1643,6 +1660,14 @@ test_dict_completion("test_repl_comp_customdict") let s = "test_dict_no_length[" @test REPLCompletions.completions(s, sizeof(s), Main.CompletionFoo) isa Tuple end + + # Issue #60444: completing dict keys should not overwrite input after cursor + let s = "test_dict[\"ab|c\"]" + c, r = test_complete_context_pos(s, Main.CompletionFoo) + @test "\"abc\"" in c + @test "\"abcd\"" in c + @test r == 11:13 # range ends at cursor, not at end of key + end end @testset "completion of string/cmd macros (#22577)" begin @@ -1789,6 +1814,13 @@ end @test hasnokwsuggestions("CompletionFoo.kwtest5('a', 3, 5, unknownsplat...; xy") @test hasnokwsuggestions("CompletionFoo.kwtest5(3; somek") =# + + # Issue #60444: completing keyword arguments should not overwrite input after cursor + let s = "CompletionFoo.kwtest3(a; foob|true)" + c, r = test_complete_pos(s) + @test c == ["foobar="] + @test r == 26:29 + end end # Test completion in context diff --git a/stdlib/SparseArrays.version b/stdlib/SparseArrays.version index 98763ae71e777..0e282a70c0acb 100644 --- a/stdlib/SparseArrays.version +++ b/stdlib/SparseArrays.version @@ -1,4 +1,4 @@ SPARSEARRAYS_BRANCH = release-1.12 -SPARSEARRAYS_SHA1 = f81a30d962b03d4048b26439d60979673e343b67 +SPARSEARRAYS_SHA1 = 2376bf8734754c5dd4264186299ae6839ab7783f SPARSEARRAYS_GIT_URL := https://github.com/JuliaSparse/SparseArrays.jl.git SPARSEARRAYS_TAR_URL = https://api.github.com/repos/JuliaSparse/SparseArrays.jl/tarball/$1 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..20f709f755915 100644 --- a/test/scopedvalues.jl +++ b/test/scopedvalues.jl @@ -195,3 +195,46 @@ nothrow_scope() push!(ts, 2) end end + +using Base.ScopedValues: ScopedValue, with +@noinline function test_59483() + sv = ScopedValue([]) + ch = Channel{Bool}() + + # Spawn a child task, which inherits the parent's Scope + @noinline function inner_function() + # Block until the parent task has left the scope. + take!(ch) + # Now, per issue 59483, this task's scope is not rooted, except by the task itself. + + # Now switch to an inner scope, leaving the current scope possibly unrooted. + val = with(sv=>Any[2]) do + # Inside this new scope, when we perform GC, the parent scope can be freed. + # The fix for this issue made sure that the first scope in this task remains + # rooted. + GC.gc() + GC.gc() + sv[] + end + @test val == Any[2] + # Finally, we've returned to the original scope, but that could be a dangling + # pointer if the scope itself was freed by the above GCs. So these GCs could crash: + GC.gc() + GC.gc() + end + @noinline function spawn_inner() + # Set a new Scope in the parent task - this is the scope that could be freed. + with(sv=>Any[1]) do + return @async inner_function() + end + end + + # RUN THE TEST: + t = spawn_inner() + # Exit the scope, and let the child task proceed + put!(ch, true) + wait(t) +end +@testset "issue 59483" begin + test_59483() +end 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