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/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/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..979034b0a1ca5 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -2305,6 +2305,19 @@ static inline jl_cgval_t value_to_pointer(jl_codectx_t &ctx, const jl_cgval_t &v Align align(julia_alignment(v.typ)); Type *ty = julia_type_to_llvm(ctx, v.typ); AllocaInst *loc = emit_static_alloca(ctx, ty, align); + jl_datatype_t *dt = (jl_datatype_t *)v.typ; + size_t npointers = dt->layout->first_ptr >= 0 ? dt->layout->npointers : 0; + if (npointers > 0) { + auto InsertPoint = ctx.builder.saveIP(); + ctx.builder.SetInsertPoint(ctx.topalloca->getParent(), ++ctx.topalloca->getIterator()); + for (size_t i = 0; i < npointers; i++) { + // make sure these are nullptr early from LLVM's perspective, in case it decides to SROA it + Value *ptr_field = emit_ptrgep(ctx, loc, jl_ptr_offset(dt, i) * sizeof(void *)); + ctx.builder.CreateAlignedStore( + Constant::getNullValue(ctx.types().T_prjlvalue), ptr_field, Align(sizeof(void *))); + } + ctx.builder.restoreIP(InsertPoint); + } auto tbaa = v.V == nullptr ? ctx.tbaa().tbaa_gcframe : ctx.tbaa().tbaa_stack; auto stack_ai = jl_aliasinfo_t::fromTBAA(ctx, tbaa); recombine_value(ctx, v, loc, stack_ai, align, false); @@ -6159,6 +6172,7 @@ static void emit_stmtpos(jl_codectx_t &ctx, jl_value_t *expr, int ssaval_result) Value *scope_ptr = get_scope_field(ctx); jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_gcframe).decorateInst( ctx.builder.CreateAlignedStore(scope_to_restore, scope_ptr, ctx.types().alignof_ptr)); + // NOTE: wb not needed here, due to store to current_task (see jl_gc_wb_current_task) } } else if (head == jl_pop_exception_sym) { @@ -7855,7 +7869,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); @@ -7931,7 +7945,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 +9348,12 @@ static jl_llvm_functions_t Value *scope_ptr = get_scope_field(ctx); LoadInst *current_scope = ctx.builder.CreateAlignedLoad(ctx.types().T_prjlvalue, scope_ptr, ctx.types().alignof_ptr); StoreInst *scope_store = ctx.builder.CreateAlignedStore(scope_boxed, scope_ptr, ctx.types().alignof_ptr); + // NOTE: wb not needed here, due to store to current_task (see jl_gc_wb_current_task) jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_gcframe).decorateInst(current_scope); jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_gcframe).decorateInst(scope_store); - // GC preserve the scope, since it is not rooted in the `jl_handler_t *` - // and may be removed from jl_current_task by any nested block and then - // replaced later - Value *scope_token = ctx.builder.CreateCall(prepare_call(gc_preserve_begin_func), {scope_boxed}); + // GC preserve the current_scope, since it is not rooted in the `jl_handler_t *`, + // the newly entered scope is preserved through the current_task. + Value *scope_token = ctx.builder.CreateCall(prepare_call(gc_preserve_begin_func), {current_scope}); ctx.scope_restore[cursor] = std::make_pair(scope_token, current_scope); } } diff --git a/src/gc-interface.h b/src/gc-interface.h index 826e91355b17a..c67b7720e70a5 100644 --- a/src/gc-interface.h +++ b/src/gc-interface.h @@ -241,6 +241,13 @@ STATIC_INLINE void jl_gc_wb(const void *parent, const void *ptr) JL_NOTSAFEPOINT // can be used to annotate that a write barrier would be required were it not for this property // (as opposed to somebody just having forgotten to think about write barriers). STATIC_INLINE void jl_gc_wb_fresh(const void *parent, const void *ptr) JL_NOTSAFEPOINT {} +// As an optimization, the current_task is explicitly added to the remset while it is running. +// Upon deschedule, we conservatively move the write barrier into the young generation. +// This allows the omission of write barriers for all GC roots on the current task stack (JL_GC_PUSH_*), +// as well as the Task's explicit fields (but only for the current task). +// This function is a no-op that can be used to annotate that a write barrier would be required were +// it not for this property (as opposed to somebody just having forgotten to think about write barriers). +STATIC_INLINE void jl_gc_wb_current_task(const void *parent JL_UNUSED, const void *ptr JL_UNUSED) JL_NOTSAFEPOINT {} // Used to annotate that a write barrier would be required, but may be omitted because `ptr` // is known to be an old object. STATIC_INLINE void jl_gc_wb_knownold(const void *parent, const void *ptr) JL_NOTSAFEPOINT {} diff --git a/src/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..9dcca5637be09 100644 --- a/src/gf.c +++ b/src/gf.c @@ -1841,7 +1841,7 @@ JL_DLLEXPORT jl_typemap_entry_t *jl_mt_find_cache_entry(jl_methcache_t *mc JL_PR return entry; } -static jl_method_instance_t *jl_mt_assoc_by_type(jl_methcache_t *mc JL_PROPAGATES_ROOT, jl_datatype_t *tt JL_MAYBE_UNROOTED, size_t world) +static jl_method_instance_t *jl_mt_assoc_by_type(jl_methtable_t *mt, jl_methcache_t *mc JL_PROPAGATES_ROOT, jl_datatype_t *tt JL_MAYBE_UNROOTED, size_t world) { jl_typemap_entry_t *entry = jl_mt_find_cache_entry(mc, tt, world); if (entry) @@ -1858,11 +1858,11 @@ static jl_method_instance_t *jl_mt_assoc_by_type(jl_methcache_t *mc JL_PROPAGATE if (!mi) { size_t min_valid = 0; size_t max_valid = ~(size_t)0; - matc = _gf_invoke_lookup((jl_value_t*)tt, jl_method_table, world, 0, &min_valid, &max_valid); + matc = _gf_invoke_lookup((jl_value_t*)tt, mt, world, 0, &min_valid, &max_valid); if (matc) { jl_method_t *m = matc->method; jl_svec_t *env = matc->sparams; - mi = cache_method(jl_method_table, mc, &mc->cache, (jl_value_t*)mc, tt, m, world, min_valid, max_valid, env); + mi = cache_method(mt, mc, &mc->cache, (jl_value_t*)mc, tt, m, world, min_valid, max_valid, env); JL_GC_POP(); return mi; } @@ -3079,7 +3079,7 @@ JL_DLLEXPORT jl_value_t *jl_method_lookup_by_tt(jl_tupletype_t *tt, size_t world mt = (jl_methtable_t*) _mt; } jl_methcache_t *mc = mt->cache; - jl_method_instance_t *mi = jl_mt_assoc_by_type(mc, tt, world); + jl_method_instance_t *mi = jl_mt_assoc_by_type(mt, mc, tt, world); if (!mi) return jl_nothing; return (jl_value_t*) mi; @@ -3094,7 +3094,7 @@ JL_DLLEXPORT jl_method_instance_t *jl_method_lookup(jl_value_t **args, size_t na if (entry) return entry->func.linfo; jl_tupletype_t *tt = arg_type_tuple(args[0], &args[1], nargs); - return jl_mt_assoc_by_type(mc, tt, world); + return jl_mt_assoc_by_type(jl_method_table, mc, tt, world); } // return a Vector{Any} of svecs, each describing a method match: @@ -4169,7 +4169,7 @@ STATIC_INLINE jl_method_instance_t *jl_lookup_generic_(jl_value_t *F, jl_value_t assert(tt); // cache miss case jl_methcache_t *mc = jl_method_table->cache; - mfunc = jl_mt_assoc_by_type(mc, tt, world); + mfunc = jl_mt_assoc_by_type(jl_method_table, mc, tt, world); if (jl_options.malloc_log) jl_gc_sync_total_bytes(last_alloc); // discard allocation count from compilation if (mfunc == NULL) { diff --git a/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/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..7005d4329c9fc 100644 --- a/src/llvm-alloc-opt.cpp +++ b/src/llvm-alloc-opt.cpp @@ -701,6 +701,7 @@ void Optimizer::moveToStack(CallInst *orig_inst, size_t sz, bool has_ref, AllocF initializeAlloca(builder, buff, allockind); } Instruction *new_inst = cast(ptr); + new_inst->copyMetadata(*orig_inst); new_inst->takeName(orig_inst); auto simple_replace = [&] (Instruction *orig_i, Instruction *new_i) { 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/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/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..d85a4f3c2eb74 100644 --- a/test/core.jl +++ b/test/core.jl @@ -8422,6 +8422,32 @@ let ms = Base._methods_by_ftype(Tuple{typeof(sin), Int}, OverlayModule.mt, 1, Ba @test isempty(ms) end +# fresh module to ensure uncached methods +module OverlayMTTest + using Base.Experimental: @MethodTable, @overlay + @MethodTable(mt) + + function overlay_only end + @overlay mt overlay_only(x::Int) = x * 2 +end + +# #60702 & #60716: Overlay methods must be found without prior cache population +let world = Base.get_world_counter() + mi = Base.method_instance(OverlayMTTest.overlay_only, Tuple{Int}; + world, method_table=OverlayMTTest.mt) + @test mi isa Core.MethodInstance + @test mi.def.module === OverlayMTTest +end + +# #60712: Global-only methods must NOT be found via custom MT +let + @eval global_only_func(x::Int) = x + 1 + world = Base.get_world_counter() + mi = Base.method_instance(global_only_func, Tuple{Int}; + world, method_table=OverlayMTTest.mt) + @test mi === nothing +end + # precompilation let load_path = mktempdir() depot_path = mkdepottempdir() 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