diff --git a/Makefile b/Makefile index 6fbbe194aa615..57d5cde328e02 100644 --- a/Makefile +++ b/Makefile @@ -398,6 +398,10 @@ endif # Install appdata file mkdir -p $(DESTDIR)$(datarootdir)/appdata/ $(INSTALL_F) $(JULIAHOME)/contrib/julia.appdata.xml $(DESTDIR)$(datarootdir)/appdata/ + # Install terminal info database +ifneq ($(WITH_TERMINFO),0) + cp -R -L $(build_datarootdir)/terminfo $(DESTDIR)$(datarootdir) +endif # Update RPATH entries and JL_SYSTEM_IMAGE_PATH if $(private_libdir_rel) != $(build_private_libdir_rel) ifneq ($(private_libdir_rel),$(build_private_libdir_rel)) diff --git a/NEWS.md b/NEWS.md index 4a9c56566d151..74255ae3d989b 100644 --- a/NEWS.md +++ b/NEWS.md @@ -63,6 +63,11 @@ Multi-threading changes * `Threads.@threads` now supports the `:greedy` scheduler, intended for non-uniform workloads ([#52096]). * A new public (but unexported) struct `Base.Lockable{T, L<:AbstractLock}` makes it easy to bundle a resource and its lock together ([#52898]). +Build system changes +-------------------- + +* There is a new `Makefile` to build Julia and LLVM using the profile-guided and link-time optimizations (PGO and LTO) strategies, see `contrib/pgo-lto/Makefile` ([#45641]). + New library functions --------------------- @@ -202,6 +207,9 @@ External dependencies * The libuv library has been updated from a base of v1.44.2 to v1.48.0 ([#49937]). * `tput` is no longer called to check terminal capabilities; it has been replaced with a pure-Julia terminfo parser ([#50797]). +- The terminal info database, `terminfo`, is now vendored by default, providing a better + REPL user experience when `terminfo` is not available on the system. Julia can be built + without vendoring the database using the Makefile option `WITH_TERMINFO=0`. ([#55411]) Tooling Improvements -------------------- @@ -220,6 +228,7 @@ Tooling Improvements [#42593]: https://github.com/JuliaLang/julia/issues/42593 [#43845]: https://github.com/JuliaLang/julia/issues/43845 [#45156]: https://github.com/JuliaLang/julia/issues/45156 +[#45641]: https://github.com/JuliaLang/julia/issues/45641 [#46501]: https://github.com/JuliaLang/julia/issues/46501 [#47354]: https://github.com/JuliaLang/julia/issues/47354 [#47679]: https://github.com/JuliaLang/julia/issues/47679 diff --git a/base/Base.jl b/base/Base.jl index 438d08e2e01d1..21c9a101758a0 100644 --- a/base/Base.jl +++ b/base/Base.jl @@ -399,7 +399,6 @@ include("weakkeydict.jl") # ScopedValues include("scopedvalues.jl") -using .ScopedValues # metaprogramming include("meta.jl") diff --git a/base/abstractarray.jl b/base/abstractarray.jl index 058297b3c3152..2d281b487c437 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -2985,7 +2985,7 @@ end @inline function _stack_size_check(x, ax1::Tuple) if _iterator_axes(x) != ax1 uax1 = map(UnitRange, ax1) - uaxN = map(UnitRange, axes(x)) + uaxN = map(UnitRange, _iterator_axes(x)) throw(DimensionMismatch( LazyString("stack expects uniform slices, got axes(x) == ", uaxN, " while first had ", uax1))) end @@ -3497,6 +3497,35 @@ julia> map(+, [1 2; 3 4], [1,10,100,1000], zeros(3,1)) # iterates until 3rd is """ map(f, it, iters...) = collect(Generator(f, it, iters...)) +# Generic versions of push! for AbstractVector +# These are specialized further for Vector for faster resizing and setindexing +function push!(a::AbstractVector{T}, item) where T + # convert first so we don't grow the array if the assignment won't work + itemT = item isa T ? item : convert(T, item)::T + new_length = length(a) + 1 + resize!(a, new_length) + a[new_length] = itemT + return a +end + +# specialize and optimize the single argument case +function push!(a::AbstractVector{Any}, @nospecialize x) + new_length = length(a) + 1 + resize!(a, new_length) + a[new_length] = x + return a +end +function push!(a::AbstractVector{Any}, @nospecialize x...) + @_terminates_locally_meta + na = length(a) + nx = length(x) + resize!(a, na + nx) + for i = 1:nx + a[na+i] = x[i] + end + return a +end + # multi-item push!, pushfirst! (built on top of type-specific 1-item version) # (note: must not cause a dispatch loop when 1-item case is not defined) push!(A, a, b) = push!(push!(A, a), b) @@ -3504,6 +3533,9 @@ push!(A, a, b, c...) = push!(push!(A, a, b), c...) pushfirst!(A, a, b) = pushfirst!(pushfirst!(A, b), a) pushfirst!(A, a, b, c...) = pushfirst!(pushfirst!(A, c...), a, b) +# sizehint! does not nothing by default +sizehint!(a::AbstractVector, _) = a + ## hashing AbstractArray ## const hash_abstractarray_seed = UInt === UInt64 ? 0x7e2d6fb6448beb77 : 0xd4514ce5 diff --git a/base/boot.jl b/base/boot.jl index 2e5c3a277262e..720819e1e2c4c 100644 --- a/base/boot.jl +++ b/base/boot.jl @@ -283,7 +283,8 @@ macro _foldable_meta() #=:inaccessiblememonly=#true, #=:noub=#true, #=:noub_if_noinbounds=#false, - #=:consistent_overlay=#false)) + #=:consistent_overlay=#false, + #=:nortcall=#true)) end macro inline() Expr(:meta, :inline) end diff --git a/base/client.jl b/base/client.jl index a0d3951391d5f..9ae18aa81eeff 100644 --- a/base/client.jl +++ b/base/client.jl @@ -608,10 +608,10 @@ module MyApp end const main = MyApp.main # `julia` Will *NOT* execute MyApp.main unless there is a separate `@main` annotation in `Main` +``` !!! compat "Julia 1.11" This macro is new in Julia 1.11. At present, the precise semantics of `@main` are still subject to change. -``` """ macro main(args...) if !isempty(args) diff --git a/base/cmd.jl b/base/cmd.jl index 202527abdf644..84ec52f865e98 100644 --- a/base/cmd.jl +++ b/base/cmd.jl @@ -482,7 +482,7 @@ function cmd_gen(parsed) end end -@assume_effects :effect_free :terminates_globally :noub function cmd_gen( +@assume_effects :foldable !:consistent function cmd_gen( parsed::Tuple{Vararg{Tuple{Vararg{Union{String, SubString{String}}}}}} ) return @invoke cmd_gen(parsed::Any) diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index ba8a352c1b528..09ea2b17b55ee 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -605,13 +605,23 @@ function abstract_call_method(interp::AbstractInterpreter, end add_remark!(interp, sv, washardlimit ? RECURSION_MSG_HARDLIMIT : RECURSION_MSG) # TODO (#48913) implement a proper recursion handling for irinterp: - # This works just because currently the `:terminate` condition guarantees that - # irinterp doesn't fail into unresolved cycles, but it's not a good solution. + # This works just because currently the `:terminate` condition usually means this is unreachable here + # for irinterp because there are not unresolved cycles, but it's not a good solution. # We should revisit this once we have a better story for handling cycles in irinterp. - if isa(topmost, InferenceState) + if isa(sv, InferenceState) + # since the hardlimit is against the edge to the parent frame, + # we should try to poison the whole edge, not just the topmost frame parentframe = frame_parent(topmost) - if isa(sv, InferenceState) && isa(parentframe, InferenceState) - poison_callstack!(sv, parentframe === nothing ? topmost : parentframe) + while !isa(parentframe, InferenceState) + # attempt to find a parent frame that can handle this LimitedAccuracy result correctly + # so we don't try to cache this incomplete intermediate result + parentframe === nothing && break + parentframe = frame_parent(parentframe) + end + if isa(parentframe, InferenceState) + poison_callstack!(sv, parentframe) + elseif isa(topmost, InferenceState) + poison_callstack!(sv, topmost) end end # n.b. this heuristic depends on the non-local state, so we must record the limit later @@ -887,7 +897,7 @@ function concrete_eval_eligible(interp::AbstractInterpreter, end end mi = result.edge - if mi !== nothing && is_foldable(effects) + if mi !== nothing && is_foldable(effects, #=check_rtcall=#true) if f !== nothing && is_all_const_arg(arginfo, #=start=#2) if (is_nonoverlayed(interp) || is_nonoverlayed(effects) || # Even if overlay methods are involved, when `:consistent_overlay` is @@ -2778,8 +2788,9 @@ function override_effects(effects::Effects, override::EffectsOverride) notaskstate = override.notaskstate ? true : effects.notaskstate, inaccessiblememonly = override.inaccessiblememonly ? ALWAYS_TRUE : effects.inaccessiblememonly, noub = override.noub ? ALWAYS_TRUE : - (override.noub_if_noinbounds && effects.noub !== ALWAYS_TRUE) ? NOUB_IF_NOINBOUNDS : - effects.noub) + (override.noub_if_noinbounds && effects.noub !== ALWAYS_TRUE) ? NOUB_IF_NOINBOUNDS : + effects.noub, + nortcall = override.nortcall ? true : effects.nortcall) end isdefined_globalref(g::GlobalRef) = !iszero(ccall(:jl_globalref_boundp, Cint, (Any,), g)) diff --git a/base/compiler/compiler.jl b/base/compiler/compiler.jl index 4e2fe3da3496f..5242d5d455cc6 100644 --- a/base/compiler/compiler.jl +++ b/base/compiler/compiler.jl @@ -48,10 +48,11 @@ struct EffectsOverride noub::Bool noub_if_noinbounds::Bool consistent_overlay::Bool + nortcall::Bool end function EffectsOverride( override::EffectsOverride = - EffectsOverride(false, false, false, false, false, false, false, false, false, false); + EffectsOverride(false, false, false, false, false, false, false, false, false, false, false); consistent::Bool = override.consistent, effect_free::Bool = override.effect_free, nothrow::Bool = override.nothrow, @@ -61,7 +62,8 @@ function EffectsOverride( inaccessiblememonly::Bool = override.inaccessiblememonly, noub::Bool = override.noub, noub_if_noinbounds::Bool = override.noub_if_noinbounds, - consistent_overlay::Bool = override.consistent_overlay) + consistent_overlay::Bool = override.consistent_overlay, + nortcall::Bool = override.nortcall) return EffectsOverride( consistent, effect_free, @@ -72,9 +74,10 @@ function EffectsOverride( inaccessiblememonly, noub, noub_if_noinbounds, - consistent_overlay) + consistent_overlay, + nortcall) end -const NUM_EFFECTS_OVERRIDES = 10 # sync with julia.h +const NUM_EFFECTS_OVERRIDES = 11 # sync with julia.h # essential files and libraries include("essentials.jl") diff --git a/base/compiler/effects.jl b/base/compiler/effects.jl index 0375b8dba922c..166df78f3130c 100644 --- a/base/compiler/effects.jl +++ b/base/compiler/effects.jl @@ -58,6 +58,9 @@ following meanings: methods are `:consistent` with their non-overlayed original counterparts (see [`Base.@assume_effects`](@ref) for the exact definition of `:consistenct`-cy). * `ALWAYS_FALSE`: this method may invoke overlayed methods. +- `nortcall::Bool`: this method does not call `Core.Compiler.return_type`, + and it is guaranteed that any other methods this method might call also do not call + `Core.Compiler.return_type`. Note that the representations above are just internal implementation details and thus likely to change in the future. See [`Base.@assume_effects`](@ref) for more detailed explanation @@ -103,6 +106,9 @@ The output represents the state of different effect properties in the following - `+o` (green): `ALWAYS_TRUE` - `-o` (red): `ALWAYS_FALSE` - `?o` (yellow): `CONSISTENT_OVERLAY` +9. `:nortcall` (`r`): + - `+r` (green): `true` + - `-r` (red): `false` """ struct Effects consistent::UInt8 @@ -113,6 +119,7 @@ struct Effects inaccessiblememonly::UInt8 noub::UInt8 nonoverlayed::UInt8 + nortcall::Bool function Effects( consistent::UInt8, effect_free::UInt8, @@ -121,7 +128,8 @@ struct Effects notaskstate::Bool, inaccessiblememonly::UInt8, noub::UInt8, - nonoverlayed::UInt8) + nonoverlayed::UInt8, + nortcall::Bool) return new( consistent, effect_free, @@ -130,7 +138,8 @@ struct Effects notaskstate, inaccessiblememonly, noub, - nonoverlayed) + nonoverlayed, + nortcall) end end @@ -160,10 +169,10 @@ const NOUB_IF_NOINBOUNDS = 0x01 << 1 # :nonoverlayed bits const CONSISTENT_OVERLAY = 0x01 << 1 -const EFFECTS_TOTAL = Effects(ALWAYS_TRUE, ALWAYS_TRUE, true, true, true, ALWAYS_TRUE, ALWAYS_TRUE, ALWAYS_TRUE) -const EFFECTS_THROWS = Effects(ALWAYS_TRUE, ALWAYS_TRUE, false, true, true, ALWAYS_TRUE, ALWAYS_TRUE, ALWAYS_TRUE) -const EFFECTS_UNKNOWN = Effects(ALWAYS_FALSE, ALWAYS_FALSE, false, false, false, ALWAYS_FALSE, ALWAYS_FALSE, ALWAYS_TRUE) # unknown mostly, but it's not overlayed at least (e.g. it's not a call) -const _EFFECTS_UNKNOWN = Effects(ALWAYS_FALSE, ALWAYS_FALSE, false, false, false, ALWAYS_FALSE, ALWAYS_FALSE, ALWAYS_FALSE) # unknown really +const EFFECTS_TOTAL = Effects(ALWAYS_TRUE, ALWAYS_TRUE, true, true, true, ALWAYS_TRUE, ALWAYS_TRUE, ALWAYS_TRUE, true) +const EFFECTS_THROWS = Effects(ALWAYS_TRUE, ALWAYS_TRUE, false, true, true, ALWAYS_TRUE, ALWAYS_TRUE, ALWAYS_TRUE, true) +const EFFECTS_UNKNOWN = Effects(ALWAYS_FALSE, ALWAYS_FALSE, false, false, false, ALWAYS_FALSE, ALWAYS_FALSE, ALWAYS_TRUE, false) # unknown mostly, but it's not overlayed at least (e.g. it's not a call) +const _EFFECTS_UNKNOWN = Effects(ALWAYS_FALSE, ALWAYS_FALSE, false, false, false, ALWAYS_FALSE, ALWAYS_FALSE, ALWAYS_FALSE, false) # unknown really function Effects(effects::Effects = _EFFECTS_UNKNOWN; consistent::UInt8 = effects.consistent, @@ -173,7 +182,8 @@ function Effects(effects::Effects = _EFFECTS_UNKNOWN; notaskstate::Bool = effects.notaskstate, inaccessiblememonly::UInt8 = effects.inaccessiblememonly, noub::UInt8 = effects.noub, - nonoverlayed::UInt8 = effects.nonoverlayed) + nonoverlayed::UInt8 = effects.nonoverlayed, + nortcall::Bool = effects.nortcall) return Effects( consistent, effect_free, @@ -182,7 +192,8 @@ function Effects(effects::Effects = _EFFECTS_UNKNOWN; notaskstate, inaccessiblememonly, noub, - nonoverlayed) + nonoverlayed, + nortcall) end function is_better_effects(new::Effects, old::Effects) @@ -247,6 +258,11 @@ function is_better_effects(new::Effects, old::Effects) elseif new.nonoverlayed != old.nonoverlayed return false end + if new.nortcall + any_improved |= !old.nortcall + elseif new.nortcall != old.nortcall + return false + end return any_improved end @@ -259,7 +275,8 @@ function merge_effects(old::Effects, new::Effects) merge_effectbits(old.notaskstate, new.notaskstate), merge_effectbits(old.inaccessiblememonly, new.inaccessiblememonly), merge_effectbits(old.noub, new.noub), - merge_effectbits(old.nonoverlayed, new.nonoverlayed)) + merge_effectbits(old.nonoverlayed, new.nonoverlayed), + merge_effectbits(old.nortcall, new.nortcall)) end function merge_effectbits(old::UInt8, new::UInt8) @@ -279,16 +296,18 @@ is_inaccessiblememonly(effects::Effects) = effects.inaccessiblememonly === ALWAY is_noub(effects::Effects) = effects.noub === ALWAYS_TRUE is_noub_if_noinbounds(effects::Effects) = effects.noub === NOUB_IF_NOINBOUNDS is_nonoverlayed(effects::Effects) = effects.nonoverlayed === ALWAYS_TRUE +is_nortcall(effects::Effects) = effects.nortcall # implies `is_notaskstate` & `is_inaccessiblememonly`, but not explicitly checked here -is_foldable(effects::Effects) = +is_foldable(effects::Effects, check_rtcall::Bool=false) = is_consistent(effects) && (is_noub(effects) || is_noub_if_noinbounds(effects)) && is_effect_free(effects) && - is_terminates(effects) + is_terminates(effects) && + (!check_rtcall || is_nortcall(effects)) -is_foldable_nothrow(effects::Effects) = - is_foldable(effects) && +is_foldable_nothrow(effects::Effects, check_rtcall::Bool=false) = + is_foldable(effects, check_rtcall) && is_nothrow(effects) # TODO add `is_noub` here? @@ -318,7 +337,8 @@ function encode_effects(e::Effects) ((e.notaskstate % UInt32) << 7) | ((e.inaccessiblememonly % UInt32) << 8) | ((e.noub % UInt32) << 10) | - ((e.nonoverlayed % UInt32) << 12) + ((e.nonoverlayed % UInt32) << 12) | + ((e.nortcall % UInt32) << 14) end function decode_effects(e::UInt32) @@ -330,7 +350,8 @@ function decode_effects(e::UInt32) _Bool((e >> 7) & 0x01), UInt8((e >> 8) & 0x03), UInt8((e >> 10) & 0x03), - UInt8((e >> 12) & 0x03)) + UInt8((e >> 12) & 0x03), + _Bool((e >> 14) & 0x01)) end function encode_effects_override(eo::EffectsOverride) @@ -345,6 +366,7 @@ function encode_effects_override(eo::EffectsOverride) eo.noub && (e |= (0x0001 << 7)) eo.noub_if_noinbounds && (e |= (0x0001 << 8)) eo.consistent_overlay && (e |= (0x0001 << 9)) + eo.nortcall && (e |= (0x0001 << 10)) return e end @@ -359,7 +381,8 @@ function decode_effects_override(e::UInt16) !iszero(e & (0x0001 << 6)), !iszero(e & (0x0001 << 7)), !iszero(e & (0x0001 << 8)), - !iszero(e & (0x0001 << 9))) + !iszero(e & (0x0001 << 9)), + !iszero(e & (0x0001 << 10))) end decode_statement_effects_override(ssaflag::UInt32) = diff --git a/base/compiler/optimize.jl b/base/compiler/optimize.jl index 3e930107b61f1..3cbf123afbd6e 100644 --- a/base/compiler/optimize.jl +++ b/base/compiler/optimize.jl @@ -44,11 +44,16 @@ const IR_FLAG_NOUB = one(UInt32) << 9 const IR_FLAG_EFIIMO = one(UInt32) << 10 # This statement is :inaccessiblememonly == INACCESSIBLEMEM_OR_ARGMEMONLY const IR_FLAG_INACCESSIBLEMEM_OR_ARGMEM = one(UInt32) << 11 +# This statement is :nortcall +const IR_FLAG_NORTCALL = one(UInt32) << 12 +# This statement has no users and may be deleted if flags get refined to IR_FLAGS_REMOVABLE +const IR_FLAG_UNUSED = one(UInt32) << 13 -const NUM_IR_FLAGS = 12 # sync with julia.h +const NUM_IR_FLAGS = 14 # sync with julia.h const IR_FLAGS_EFFECTS = - IR_FLAG_CONSISTENT | IR_FLAG_EFFECT_FREE | IR_FLAG_NOTHROW | IR_FLAG_NOUB + IR_FLAG_CONSISTENT | IR_FLAG_EFFECT_FREE | IR_FLAG_NOTHROW | + IR_FLAG_NOUB | IR_FLAG_NORTCALL const IR_FLAGS_REMOVABLE = IR_FLAG_EFFECT_FREE | IR_FLAG_NOTHROW @@ -75,6 +80,9 @@ function flags_for_effects(effects::Effects) if is_noub(effects) flags |= IR_FLAG_NOUB end + if is_nortcall(effects) + flags |= IR_FLAG_NORTCALL + end return flags end @@ -597,26 +605,28 @@ mutable struct PostOptAnalysisState all_nothrow::Bool all_noub::Bool any_conditional_ub::Bool + nortcall::Bool function PostOptAnalysisState(result::InferenceResult, ir::IRCode) inconsistent = BitSetBoundedMinPrioritySet(length(ir.stmts)) tpdum = TwoPhaseDefUseMap(length(ir.stmts)) lazypostdomtree = LazyPostDomtree(ir) lazyagdomtree = LazyAugmentedDomtree(ir) return new(result, ir, inconsistent, tpdum, lazypostdomtree, lazyagdomtree, Int[], - true, true, nothing, true, true, false) + true, true, nothing, true, true, false, true) end end give_up_refinements!(sv::PostOptAnalysisState) = sv.all_retpaths_consistent = sv.all_effect_free = sv.effect_free_if_argmem_only = - sv.all_nothrow = sv.all_noub = false + sv.all_nothrow = sv.all_noub = sv.nortcall = false function any_refinable(sv::PostOptAnalysisState) effects = sv.result.ipo_effects return ((!is_consistent(effects) & sv.all_retpaths_consistent) | (!is_effect_free(effects) & sv.all_effect_free) | (!is_nothrow(effects) & sv.all_nothrow) | - (!is_noub(effects) & sv.all_noub)) + (!is_noub(effects) & sv.all_noub) | + (!is_nortcall(effects) & sv.nortcall)) end struct GetNativeEscapeCache{CodeCache} @@ -661,7 +671,8 @@ function refine_effects!(interp::AbstractInterpreter, sv::PostOptAnalysisState) effect_free = sv.all_effect_free ? ALWAYS_TRUE : sv.effect_free_if_argmem_only === true ? EFFECT_FREE_IF_INACCESSIBLEMEMONLY : effects.effect_free, nothrow = sv.all_nothrow ? true : effects.nothrow, - noub = sv.all_noub ? (sv.any_conditional_ub ? NOUB_IF_NOINBOUNDS : ALWAYS_TRUE) : effects.noub) + noub = sv.all_noub ? (sv.any_conditional_ub ? NOUB_IF_NOINBOUNDS : ALWAYS_TRUE) : effects.noub, + nortcall = sv.nortcall ? true : effects.nortcall) return true end @@ -786,6 +797,13 @@ function scan_non_dataflow_flags!(inst::Instruction, sv::PostOptAnalysisState) sv.all_noub = false end end + if !has_flag(flag, IR_FLAG_NORTCALL) + # if a function call that might invoke `Core.Compiler.return_type` has been deleted, + # there's no need to taint with `:nortcall`, allowing concrete evaluation + if isexpr(stmt, :call) || isexpr(stmt, :invoke) + sv.nortcall = false + end + end end function scan_inconsistency!(inst::Instruction, sv::PostOptAnalysisState) diff --git a/base/compiler/ssair/show.jl b/base/compiler/ssair/show.jl index 873ab2c68f7a8..2843b7e3c12fb 100644 --- a/base/compiler/ssair/show.jl +++ b/base/compiler/ssair/show.jl @@ -1021,6 +1021,8 @@ function Base.show(io::IO, e::Effects) printstyled(io, effectbits_letter(e, :noub, 'u'); color=effectbits_color(e, :noub)) print(io, ',') printstyled(io, effectbits_letter(e, :nonoverlayed, 'o'); color=effectbits_color(e, :nonoverlayed)) + print(io, ',') + printstyled(io, effectbits_letter(e, :nortcall, 'r'); color=effectbits_color(e, :nortcall)) print(io, ')') end diff --git a/base/compiler/tfuncs.jl b/base/compiler/tfuncs.jl index 8b778b86db8f0..1fded9d33b178 100644 --- a/base/compiler/tfuncs.jl +++ b/base/compiler/tfuncs.jl @@ -2922,7 +2922,7 @@ end # since abstract_call_gf_by_type is a very inaccurate model of _method and of typeinf_type, # while this assumes that it is an absolutely precise and accurate and exact model of both function return_type_tfunc(interp::AbstractInterpreter, argtypes::Vector{Any}, si::StmtInfo, sv::AbsIntState) - UNKNOWN = CallMeta(Type, Any, EFFECTS_THROWS, NoCallInfo()) + UNKNOWN = CallMeta(Type, Any, Effects(EFFECTS_THROWS; nortcall=false), NoCallInfo()) if !(2 <= length(argtypes) <= 3) return UNKNOWN end @@ -2950,8 +2950,12 @@ function return_type_tfunc(interp::AbstractInterpreter, argtypes::Vector{Any}, s return UNKNOWN end + # effects are not an issue if we know this statement will get removed, but if it does not get removed, + # then this could be recursively re-entering inference (via concrete-eval), which will not terminate + RT_CALL_EFFECTS = Effects(EFFECTS_TOTAL; nortcall=false) + if contains_is(argtypes_vec, Union{}) - return CallMeta(Const(Union{}), Union{}, EFFECTS_TOTAL, NoCallInfo()) + return CallMeta(Const(Union{}), Union{}, RT_CALL_EFFECTS, NoCallInfo()) end # Run the abstract_call without restricting abstract call @@ -2969,25 +2973,25 @@ function return_type_tfunc(interp::AbstractInterpreter, argtypes::Vector{Any}, s rt = widenslotwrapper(call.rt) if isa(rt, Const) # output was computed to be constant - return CallMeta(Const(typeof(rt.val)), Union{}, EFFECTS_TOTAL, info) + return CallMeta(Const(typeof(rt.val)), Union{}, RT_CALL_EFFECTS, info) end rt = widenconst(rt) if rt === Bottom || (isconcretetype(rt) && !iskindtype(rt)) # output cannot be improved so it is known for certain - return CallMeta(Const(rt), Union{}, EFFECTS_TOTAL, info) + return CallMeta(Const(rt), Union{}, RT_CALL_EFFECTS, info) elseif isa(sv, InferenceState) && !isempty(sv.pclimitations) # conservatively express uncertainty of this result # in two ways: both as being a subtype of this, and # because of LimitedAccuracy causes - return CallMeta(Type{<:rt}, Union{}, EFFECTS_TOTAL, info) + return CallMeta(Type{<:rt}, Union{}, RT_CALL_EFFECTS, info) elseif isa(tt, Const) || isconstType(tt) # input arguments were known for certain # XXX: this doesn't imply we know anything about rt - return CallMeta(Const(rt), Union{}, EFFECTS_TOTAL, info) + return CallMeta(Const(rt), Union{}, RT_CALL_EFFECTS, info) elseif isType(rt) - return CallMeta(Type{rt}, Union{}, EFFECTS_TOTAL, info) + return CallMeta(Type{rt}, Union{}, RT_CALL_EFFECTS, info) else - return CallMeta(Type{<:rt}, Union{}, EFFECTS_TOTAL, info) + return CallMeta(Type{<:rt}, Union{}, RT_CALL_EFFECTS, info) end end diff --git a/base/compiler/typeinfer.jl b/base/compiler/typeinfer.jl index 83575cd6f9278..f3f6c38125628 100644 --- a/base/compiler/typeinfer.jl +++ b/base/compiler/typeinfer.jl @@ -495,6 +495,9 @@ function adjust_effects(ipo_effects::Effects, def::Method) if is_effect_overridden(override, :consistent_overlay) ipo_effects = Effects(ipo_effects; nonoverlayed=CONSISTENT_OVERLAY) end + if is_effect_overridden(override, :nortcall) + ipo_effects = Effects(ipo_effects; nortcall=true) + end return ipo_effects end diff --git a/base/complex.jl b/base/complex.jl index 8ac126d2c6532..095c842795d38 100644 --- a/base/complex.jl +++ b/base/complex.jl @@ -1037,24 +1037,22 @@ end function atanh(z::Complex{T}) where T z = float(z) Tf = float(T) - Ω = prevfloat(typemax(Tf)) - θ = sqrt(Ω)/4 - ρ = 1/θ x, y = reim(z) ax = abs(x) ay = abs(y) + θ = sqrt(floatmax(Tf))/4 if ax > θ || ay > θ #Prevent overflow if isnan(y) if isinf(x) return Complex(copysign(zero(x),x), y) else - return Complex(real(1/z), y) + return Complex(real(inv(z)), y) end end if isinf(y) return Complex(copysign(zero(x),x), copysign(oftype(y,pi)/2, y)) end - return Complex(real(1/z), copysign(oftype(y,pi)/2, y)) + return Complex(real(inv(z)), copysign(oftype(y,pi)/2, y)) end β = copysign(one(Tf), x) z *= β @@ -1064,16 +1062,15 @@ function atanh(z::Complex{T}) where T ξ = oftype(x, Inf) η = y else - ym = ay+ρ - ξ = log(sqrt(sqrt(4+y*y))/sqrt(ym)) - η = copysign(oftype(y,pi)/2 + atan(ym/2), y)/2 + ξ = log(sqrt(sqrt(muladd(y, y, 4)))/sqrt(ay)) + η = copysign(oftype(y,pi)/2 + atan(ay/2), y)/2 end else #Normal case - ysq = (ay+ρ)^2 + ysq = ay^2 if x == 0 ξ = x else - ξ = log1p(4x/((1-x)^2 + ysq))/4 + ξ = log1p(4x/(muladd(1-x, 1-x, ysq)))/4 end η = angle(Complex((1-x)*(1+x)-ysq, 2y))/2 end diff --git a/base/deprecated.jl b/base/deprecated.jl index 7e7fa5596ca25..8dc037b92bb67 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -10,9 +10,7 @@ # and of exporting the function. # # For more complex cases, move the body of the deprecated method in this file, -# and call depwarn() directly from inside it. The symbol depwarn() expects is -# the name of the function, which is used to ensure that the deprecation warning -# is only printed the first time for each call place. +# and call depwarn() directly from inside it. """ @deprecate old new [export_old=true] @@ -22,6 +20,8 @@ with the specified signature in the process. To prevent `old` from being exported, set `export_old` to `false`. +See also [`Base.depwarn()`](@ref). + !!! compat "Julia 1.5" As of Julia 1.5, functions defined by `@deprecate` do not print warning when `julia` is run without the `--depwarn=yes` flag set, as the default value of `--depwarn` option @@ -118,6 +118,26 @@ macro deprecate(old, new, export_old=true) end end +""" + Base.depwarn(msg::String, funcsym::Symbol; force=false) + +Print `msg` as a deprecation warning. The symbol `funcsym` should be the name +of the calling function, which is used to ensure that the deprecation warning is +only printed the first time for each call place. Set `force=true` to force the +warning to always be shown, even if Julia was started with `--depwarn=no` (the +default). + +See also [`@deprecate`](@ref). + +# Examples +```julia +function deprecated_func() + Base.depwarn("Don't use `deprecated_func()`!", :deprecated_func) + + 1 + 1 +end +``` +""" @nospecializeinfer function depwarn(msg, funcsym; force::Bool=false) @nospecialize # N.B. With this use of `@invokelatest`, we're preventing the addition of backedges from diff --git a/base/essentials.jl b/base/essentials.jl index fd2a4af30e51d..badb7b0853b3b 100644 --- a/base/essentials.jl +++ b/base/essentials.jl @@ -213,7 +213,8 @@ macro _total_meta() #=:inaccessiblememonly=#true, #=:noub=#true, #=:noub_if_noinbounds=#false, - #=:consistent_overlay=#false)) + #=:consistent_overlay=#false, + #=:nortcall=#true)) end # can be used in place of `@assume_effects :foldable` (supposed to be used for bootstrapping) macro _foldable_meta() @@ -227,7 +228,8 @@ macro _foldable_meta() #=:inaccessiblememonly=#true, #=:noub=#true, #=:noub_if_noinbounds=#false, - #=:consistent_overlay=#false)) + #=:consistent_overlay=#false, + #=:nortcall=#true)) end # can be used in place of `@assume_effects :terminates_locally` (supposed to be used for bootstrapping) macro _terminates_locally_meta() @@ -241,7 +243,8 @@ macro _terminates_locally_meta() #=:inaccessiblememonly=#false, #=:noub=#false, #=:noub_if_noinbounds=#false, - #=:consistent_overlay=#false)) + #=:consistent_overlay=#false, + #=:nortcall=#false)) end # can be used in place of `@assume_effects :terminates_globally` (supposed to be used for bootstrapping) macro _terminates_globally_meta() @@ -255,7 +258,8 @@ macro _terminates_globally_meta() #=:inaccessiblememonly=#false, #=:noub=#false, #=:noub_if_noinbounds=#false, - #=:consistent_overlay=#false)) + #=:consistent_overlay=#false, + #=:nortcall=#false)) end # can be used in place of `@assume_effects :terminates_globally :notaskstate` (supposed to be used for bootstrapping) macro _terminates_globally_notaskstate_meta() @@ -269,7 +273,8 @@ macro _terminates_globally_notaskstate_meta() #=:inaccessiblememonly=#false, #=:noub=#false, #=:noub_if_noinbounds=#false, - #=:consistent_overlay=#false)) + #=:consistent_overlay=#false, + #=:nortcall=#false)) end # can be used in place of `@assume_effects :terminates_globally :noub` (supposed to be used for bootstrapping) macro _terminates_globally_noub_meta() @@ -283,7 +288,8 @@ macro _terminates_globally_noub_meta() #=:inaccessiblememonly=#false, #=:noub=#true, #=:noub_if_noinbounds=#false, - #=:consistent_overlay=#false)) + #=:consistent_overlay=#false, + #=:nortcall=#false)) end # can be used in place of `@assume_effects :effect_free :terminates_locally` (supposed to be used for bootstrapping) macro _effect_free_terminates_locally_meta() @@ -297,7 +303,8 @@ macro _effect_free_terminates_locally_meta() #=:inaccessiblememonly=#false, #=:noub=#false, #=:noub_if_noinbounds=#false, - #=:consistent_overlay=#false)) + #=:consistent_overlay=#false, + #=:nortcall=#false)) end # can be used in place of `@assume_effects :nothrow :noub` (supposed to be used for bootstrapping) macro _nothrow_noub_meta() @@ -311,7 +318,8 @@ macro _nothrow_noub_meta() #=:inaccessiblememonly=#false, #=:noub=#true, #=:noub_if_noinbounds=#false, - #=:consistent_overlay=#false)) + #=:consistent_overlay=#false, + #=:nortcall=#false)) end # can be used in place of `@assume_effects :nothrow` (supposed to be used for bootstrapping) macro _nothrow_meta() @@ -325,7 +333,8 @@ macro _nothrow_meta() #=:inaccessiblememonly=#false, #=:noub=#false, #=:noub_if_noinbounds=#false, - #=:consistent_overlay=#false)) + #=:consistent_overlay=#false, + #=:nortcall=#false)) end # can be used in place of `@assume_effects :nothrow` (supposed to be used for bootstrapping) macro _noub_meta() @@ -353,7 +362,8 @@ macro _notaskstate_meta() #=:inaccessiblememonly=#false, #=:noub=#false, #=:noub_if_noinbounds=#false, - #=:consistent_overlay=#false)) + #=:consistent_overlay=#false, + #=:nortcall=#false)) end # can be used in place of `@assume_effects :noub_if_noinbounds` (supposed to be used for bootstrapping) macro _noub_if_noinbounds_meta() @@ -367,7 +377,8 @@ macro _noub_if_noinbounds_meta() #=:inaccessiblememonly=#false, #=:noub=#false, #=:noub_if_noinbounds=#true, - #=:consistent_overlay=#false)) + #=:consistent_overlay=#false, + #=:nortcall=#false)) end # another version of inlining that propagates an inbounds context diff --git a/base/exports.jl b/base/exports.jl index 888587b30a0c5..3bacc5cd10aff 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -1177,4 +1177,5 @@ public # misc notnothing, runtests, - text_colors + text_colors, + depwarn diff --git a/base/expr.jl b/base/expr.jl index f39a46b492d90..c4ed916fe34aa 100644 --- a/base/expr.jl +++ b/base/expr.jl @@ -510,6 +510,7 @@ The following `setting`s are supported. - `:inaccessiblememonly` - `:noub` - `:noub_if_noinbounds` +- `:nortcall` - `:foldable` - `:removable` - `:total` @@ -678,6 +679,20 @@ The `:noub` setting asserts that the method will not execute any undefined behav any other effect assertions (such as `:consistent` or `:effect_free`) as well, but we do not model this, and they assume the absence of undefined behavior. +--- +## `:nortcall` + +The `:nortcall` setting asserts that the method does not call `Core.Compiler.return_type`, +and that any other methods this method might call also do not call `Core.Compiler.return_type`. + +!!! note + To be precise, this assertion can be used when a call to `Core.Compiler.return_type` is + not made at runtime; that is, when the result of `Core.Compiler.return_type` is known + exactly at compile time and the call is eliminated by the optimizer. However, since + whether the result of `Core.Compiler.return_type` is folded at compile time depends + heavily on the compiler's implementation, it is generally risky to assert this if + the method in question uses `Core.Compiler.return_type` in any form. + --- ## `:foldable` @@ -688,6 +703,7 @@ currently equivalent to the following `setting`s: - `:effect_free` - `:terminates_globally` - `:noub` +- `:nortcall` !!! note This list in particular does not include `:nothrow`. The compiler will still @@ -721,6 +737,7 @@ the following other `setting`s: - `:notaskstate` - `:inaccessiblememonly` - `:noub` +- `:nortcall` !!! warning `:total` is a very strong assertion and will likely gain additional semantics @@ -799,17 +816,17 @@ function compute_assumed_setting(override::EffectsOverride, @nospecialize(settin elseif setting === :noub_if_noinbounds return EffectsOverride(override; noub_if_noinbounds = val) elseif setting === :foldable - consistent = effect_free = terminates_globally = noub = val - return EffectsOverride(override; consistent, effect_free, terminates_globally, noub) + consistent = effect_free = terminates_globally = noub = nortcall = val + return EffectsOverride(override; consistent, effect_free, terminates_globally, noub, nortcall) elseif setting === :removable effect_free = nothrow = terminates_globally = val return EffectsOverride(override; effect_free, nothrow, terminates_globally) elseif setting === :total consistent = effect_free = nothrow = terminates_globally = notaskstate = - inaccessiblememonly = noub = val + inaccessiblememonly = noub = nortcall = val return EffectsOverride(override; consistent, effect_free, nothrow, terminates_globally, notaskstate, - inaccessiblememonly, noub) + inaccessiblememonly, noub, nortcall) end return nothing end diff --git a/base/intfuncs.jl b/base/intfuncs.jl index e27c5f665b3a8..74b450990bc12 100644 --- a/base/intfuncs.jl +++ b/base/intfuncs.jl @@ -1234,3 +1234,102 @@ function binomial(x::Number, k::Integer) # and instead divide each term by i, to avoid spurious overflow. return prod(i -> (x-(i-1))/i, OneTo(k), init=oneunit(x)/one(k)) end + +""" + clamp(x, lo, hi) + +Return `x` if `lo <= x <= hi`. If `x > hi`, return `hi`. If `x < lo`, return `lo`. Arguments +are promoted to a common type. + +See also [`clamp!`](@ref), [`min`](@ref), [`max`](@ref). + +!!! compat "Julia 1.3" + `missing` as the first argument requires at least Julia 1.3. + +# Examples +```jldoctest +julia> clamp.([pi, 1.0, big(10)], 2.0, 9.0) +3-element Vector{BigFloat}: + 3.141592653589793238462643383279502884197169399375105820974944592307816406286198 + 2.0 + 9.0 + +julia> clamp.([11, 8, 5], 10, 6) # an example where lo > hi +3-element Vector{Int64}: + 6 + 6 + 10 +``` +""" +function clamp(x::X, lo::L, hi::H) where {X,L,H} + T = promote_type(X, L, H) + return (x > hi) ? convert(T, hi) : (x < lo) ? convert(T, lo) : convert(T, x) +end + +""" + clamp(x, T)::T + +Clamp `x` between `typemin(T)` and `typemax(T)` and convert the result to type `T`. + +See also [`trunc`](@ref). + +# Examples +```jldoctest +julia> clamp(200, Int8) +127 + +julia> clamp(-200, Int8) +-128 + +julia> trunc(Int, 4pi^2) +39 +``` +""" +function clamp(x, ::Type{T}) where {T<:Integer} + # delegating to clamp(x, typemin(T), typemax(T)) would promote types + # this way, we avoid unnecessary conversions + # think of, e.g., clamp(big(2) ^ 200, Int16) + lo = typemin(T) + hi = typemax(T) + return (x > hi) ? hi : (x < lo) ? lo : convert(T, x) +end + + +""" + clamp!(array::AbstractArray, lo, hi) + +Restrict values in `array` to the specified range, in-place. +See also [`clamp`](@ref). + +!!! compat "Julia 1.3" + `missing` entries in `array` require at least Julia 1.3. + +# Examples +```jldoctest +julia> row = collect(-4:4)'; + +julia> clamp!(row, 0, Inf) +1×9 adjoint(::Vector{Int64}) with eltype Int64: + 0 0 0 0 0 1 2 3 4 + +julia> clamp.((-4:4)', 0, Inf) +1×9 Matrix{Float64}: + 0.0 0.0 0.0 0.0 0.0 1.0 2.0 3.0 4.0 +``` +""" +function clamp!(x::AbstractArray, lo, hi) + @inbounds for i in eachindex(x) + x[i] = clamp(x[i], lo, hi) + end + x +end + +""" + clamp(x::Integer, r::AbstractUnitRange) + +Clamp `x` to lie within range `r`. + +!!! compat "Julia 1.6" + This method requires at least Julia 1.6. +""" +clamp(x::Integer, r::AbstractUnitRange{<:Integer}) = clamp(x, first(r), last(r)) diff --git a/base/io.jl b/base/io.jl index a0b80f6f3e056..caac8eee29991 100644 --- a/base/io.jl +++ b/base/io.jl @@ -543,8 +543,8 @@ julia> rm("my_file.txt") ``` """ readuntil(filename::AbstractString, delim; kw...) = open(io->readuntil(io, delim; kw...), convert(String, filename)::String) -readuntil(stream::IO, delim::UInt8; kw...) = _unsafe_take!(copyuntil(IOBuffer(sizehint=70), stream, delim; kw...)) -readuntil(stream::IO, delim::Union{AbstractChar, AbstractString}; kw...) = String(_unsafe_take!(copyuntil(IOBuffer(sizehint=70), stream, delim; kw...))) +readuntil(stream::IO, delim::UInt8; kw...) = _unsafe_take!(copyuntil(IOBuffer(sizehint=16), stream, delim; kw...)) +readuntil(stream::IO, delim::Union{AbstractChar, AbstractString}; kw...) = String(_unsafe_take!(copyuntil(IOBuffer(sizehint=16), stream, delim; kw...))) readuntil(stream::IO, delim::T; keep::Bool=false) where T = _copyuntil(Vector{T}(), stream, delim, keep) @@ -617,7 +617,7 @@ Logan readline(filename::AbstractString; keep::Bool=false) = open(io -> readline(io; keep), filename) readline(s::IO=stdin; keep::Bool=false) = - String(_unsafe_take!(copyline(IOBuffer(sizehint=70), s; keep))) + String(_unsafe_take!(copyline(IOBuffer(sizehint=16), s; keep))) """ copyline(out::IO, io::IO=stdin; keep::Bool=false) @@ -1111,7 +1111,7 @@ function copyuntil(out::IO, io::IO, target::AbstractString; keep::Bool=false) end function readuntil(io::IO, target::AbstractVector{T}; keep::Bool=false) where T - out = (T === UInt8 ? resize!(StringVector(70), 0) : Vector{T}()) + out = (T === UInt8 ? resize!(StringVector(16), 0) : Vector{T}()) readuntil_vector!(io, target, keep, out) return out end diff --git a/base/loading.jl b/base/loading.jl index 30aa182bd95ef..5614c3603e540 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -264,11 +264,15 @@ const LOADING_CACHE = Ref{Union{LoadingCache, Nothing}}(nothing) LoadingCache() = LoadingCache(load_path(), Dict(), Dict(), Dict(), Set(), Dict(), Dict(), Dict()) -struct TOMLCache - p::TOML.Parser +struct TOMLCache{Dates} + p::TOML.Parser{Dates} d::Dict{String, CachedTOMLDict} end -const TOML_CACHE = TOMLCache(TOML.Parser(), Dict{String, Dict{String, Any}}()) +TOMLCache(p::TOML.Parser) = TOMLCache(p, Dict{String, CachedTOMLDict}()) +# TODO: Delete this converting constructor once Pkg stops using it +TOMLCache(p::TOML.Parser, d::Dict{String, Dict{String, Any}}) = TOMLCache(p, convert(Dict{String, CachedTOMLDict}, d)) + +const TOML_CACHE = TOMLCache(TOML.Parser{nothing}()) parsed_toml(project_file::AbstractString) = parsed_toml(project_file, TOML_CACHE, require_lock) function parsed_toml(project_file::AbstractString, toml_cache::TOMLCache, toml_lock::ReentrantLock) @@ -2811,7 +2815,7 @@ function create_expr_cache(pkg::PkgId, input::String, output::String, output_o:: opts = `-O0 --output-ji $(output) --output-incremental=yes` end - trace = isassigned(PRECOMPILE_TRACE_COMPILE) ? `--trace-compile=$(PRECOMPILE_TRACE_COMPILE[])` : `` + trace = isassigned(PRECOMPILE_TRACE_COMPILE) ? `--trace-compile=$(PRECOMPILE_TRACE_COMPILE[]) --trace-compile-timing` : `` io = open(pipeline(addenv(`$(julia_cmd(;cpu_target)::Cmd) $(flags) $(opts) @@ -3586,7 +3590,13 @@ end ignore_loaded::Bool=false, requested_flags::CacheFlags=CacheFlags(), reasons::Union{Dict{String,Int},Nothing}=nothing, stalecheck::Bool=true) # n.b.: this function does nearly all of the file validation, not just those checks related to stale, so the name is potentially unclear - io = open(cachefile, "r") + io = try + open(cachefile, "r") + catch ex + ex isa IOError || ex isa SystemError || rethrow() + @debug "Rejecting cache file $cachefile for $modkey because it could not be opened" isfile(cachefile) + return true + end try checksum = isvalid_cache_header(io) if iszero(checksum) diff --git a/base/lock.jl b/base/lock.jl index 7cbb023a78ee4..b69f3c5c03638 100644 --- a/base/lock.jl +++ b/base/lock.jl @@ -498,10 +498,10 @@ This provides an acquire & release memory ordering on notify/wait. The `autoreset` functionality and memory ordering guarantee requires at least Julia 1.8. """ mutable struct Event - const notify::ThreadSynchronizer + const notify::Threads.Condition const autoreset::Bool @atomic set::Bool - Event(autoreset::Bool=false) = new(ThreadSynchronizer(), autoreset, false) + Event(autoreset::Bool=false) = new(Threads.Condition(), autoreset, false) end function wait(e::Event) diff --git a/base/logging/logging.jl b/base/logging/logging.jl index 7124ffe25abf2..c1602abcdbe4a 100644 --- a/base/logging/logging.jl +++ b/base/logging/logging.jl @@ -3,7 +3,7 @@ module CoreLogging import Base: isless, +, -, convert, show -import Base: ScopedValue, with, @with +import Base.ScopedValues: ScopedValue, with, @with export AbstractLogger, diff --git a/base/math.jl b/base/math.jl index 4626a684d5f0e..5266cff8d47fc 100644 --- a/base/math.jl +++ b/base/math.jl @@ -23,7 +23,7 @@ import .Base: log, exp, sin, cos, tan, sinh, cosh, tanh, asin, using .Base: sign_mask, exponent_mask, exponent_one, exponent_half, uinttype, significand_mask, significand_bits, exponent_bits, exponent_bias, - exponent_max, exponent_raw_max + exponent_max, exponent_raw_max, clamp, clamp! using Core.Intrinsics: sqrt_llvm @@ -69,104 +69,6 @@ end return Txy, T(xy-Txy) end -""" - clamp(x, lo, hi) - -Return `x` if `lo <= x <= hi`. If `x > hi`, return `hi`. If `x < lo`, return `lo`. Arguments -are promoted to a common type. - -See also [`clamp!`](@ref), [`min`](@ref), [`max`](@ref). - -!!! compat "Julia 1.3" - `missing` as the first argument requires at least Julia 1.3. - -# Examples -```jldoctest -julia> clamp.([pi, 1.0, big(10)], 2.0, 9.0) -3-element Vector{BigFloat}: - 3.141592653589793238462643383279502884197169399375105820974944592307816406286198 - 2.0 - 9.0 - -julia> clamp.([11, 8, 5], 10, 6) # an example where lo > hi -3-element Vector{Int64}: - 6 - 6 - 10 -``` -""" -function clamp(x::X, lo::L, hi::H) where {X,L,H} - T = promote_type(X, L, H) - return (x > hi) ? convert(T, hi) : (x < lo) ? convert(T, lo) : convert(T, x) -end - -""" - clamp(x, T)::T - -Clamp `x` between `typemin(T)` and `typemax(T)` and convert the result to type `T`. - -See also [`trunc`](@ref). - -# Examples -```jldoctest -julia> clamp(200, Int8) -127 - -julia> clamp(-200, Int8) --128 - -julia> trunc(Int, 4pi^2) -39 -``` -""" -function clamp(x, ::Type{T}) where {T<:Integer} - # delegating to clamp(x, typemin(T), typemax(T)) would promote types - # this way, we avoid unnecessary conversions - # think of, e.g., clamp(big(2) ^ 200, Int16) - lo = typemin(T) - hi = typemax(T) - return (x > hi) ? hi : (x < lo) ? lo : convert(T, x) -end - - -""" - clamp!(array::AbstractArray, lo, hi) - -Restrict values in `array` to the specified range, in-place. -See also [`clamp`](@ref). - -!!! compat "Julia 1.3" - `missing` entries in `array` require at least Julia 1.3. - -# Examples -```jldoctest -julia> row = collect(-4:4)'; - -julia> clamp!(row, 0, Inf) -1×9 adjoint(::Vector{Int64}) with eltype Int64: - 0 0 0 0 0 1 2 3 4 - -julia> clamp.((-4:4)', 0, Inf) -1×9 Matrix{Float64}: - 0.0 0.0 0.0 0.0 0.0 1.0 2.0 3.0 4.0 -``` -""" -function clamp!(x::AbstractArray, lo, hi) - @inbounds for i in eachindex(x) - x[i] = clamp(x[i], lo, hi) - end - x -end - -""" - clamp(x::Integer, r::AbstractUnitRange) - -Clamp `x` to lie within range `r`. - -!!! compat "Julia 1.6" - This method requires at least Julia 1.6. -""" -clamp(x::Integer, r::AbstractUnitRange{<:Integer}) = clamp(x, first(r), last(r)) """ evalpoly(x, p) @@ -1639,7 +1541,6 @@ end exp2(x::AbstractFloat) = 2^x exp10(x::AbstractFloat) = 10^x -clamp(::Missing, lo, hi) = missing fourthroot(::Missing) = missing end # module diff --git a/base/missing.jl b/base/missing.jl index ce174edc297e3..1f34195efed88 100644 --- a/base/missing.jl +++ b/base/missing.jl @@ -135,6 +135,7 @@ min(::Any, ::Missing) = missing max(::Missing, ::Missing) = missing max(::Missing, ::Any) = missing max(::Any, ::Missing) = missing +clamp(::Missing, lo, hi) = missing missing_conversion_msg(@nospecialize T) = LazyString("cannot convert a missing value to type ", T, ": use Union{", T, ", Missing} instead") diff --git a/base/reduce.jl b/base/reduce.jl index 6a0d46c61fcd9..5044be6095cae 100644 --- a/base/reduce.jl +++ b/base/reduce.jl @@ -646,11 +646,11 @@ function mapreduce_impl(f, op::Union{typeof(max), typeof(min)}, start = first + 1 simdstop = start + chunk_len - 4 while simdstop <= last - 3 - @inbounds for i in start:4:simdstop - v1 = _fast(op, v1, f(A[i+0])) - v2 = _fast(op, v2, f(A[i+1])) - v3 = _fast(op, v3, f(A[i+2])) - v4 = _fast(op, v4, f(A[i+3])) + for i in start:4:simdstop + v1 = _fast(op, v1, f(@inbounds(A[i+0]))) + v2 = _fast(op, v2, f(@inbounds(A[i+1]))) + v3 = _fast(op, v3, f(@inbounds(A[i+2]))) + v4 = _fast(op, v4, f(@inbounds(A[i+3]))) end checkbounds(A, simdstop+3) start += chunk_len diff --git a/base/reducedim.jl b/base/reducedim.jl index 1a7a6feb33d5e..19d8f0a5af77c 100644 --- a/base/reducedim.jl +++ b/base/reducedim.jl @@ -272,19 +272,20 @@ function _mapreducedim!(f, op, R::AbstractArray, A::AbstractArrayOrBroadcasted) if reducedim1(R, A) # keep the accumulator as a local variable when reducing along the first dimension i1 = first(axes1(R)) - @inbounds for IA in CartesianIndices(indsAt) + for IA in CartesianIndices(indsAt) IR = Broadcast.newindex(IA, keep, Idefault) - r = R[i1,IR] + @inbounds r = R[i1,IR] @simd for i in axes(A, 1) - r = op(r, f(A[i, IA])) + r = op(r, f(@inbounds(A[i, IA]))) end - R[i1,IR] = r + @inbounds R[i1,IR] = r end else - @inbounds for IA in CartesianIndices(indsAt) + for IA in CartesianIndices(indsAt) IR = Broadcast.newindex(IA, keep, Idefault) @simd for i in axes(A, 1) - R[i,IR] = op(R[i,IR], f(A[i,IA])) + v = op(@inbounds(R[i,IR]), f(@inbounds(A[i,IA]))) + @inbounds R[i,IR] = v end end end @@ -1028,33 +1029,33 @@ function findminmax!(f, op, Rval, Rind, A::AbstractArray{T,N}) where {T,N} zi = zero(eltype(ks)) if reducedim1(Rval, A) i1 = first(axes1(Rval)) - @inbounds for IA in CartesianIndices(indsAt) + for IA in CartesianIndices(indsAt) IR = Broadcast.newindex(IA, keep, Idefault) - tmpRv = Rval[i1,IR] - tmpRi = Rind[i1,IR] + @inbounds tmpRv = Rval[i1,IR] + @inbounds tmpRi = Rind[i1,IR] for i in axes(A,1) k, kss = y::Tuple - tmpAv = f(A[i,IA]) + tmpAv = f(@inbounds(A[i,IA])) if tmpRi == zi || op(tmpRv, tmpAv) tmpRv = tmpAv tmpRi = k end y = iterate(ks, kss) end - Rval[i1,IR] = tmpRv - Rind[i1,IR] = tmpRi + @inbounds Rval[i1,IR] = tmpRv + @inbounds Rind[i1,IR] = tmpRi end else - @inbounds for IA in CartesianIndices(indsAt) + for IA in CartesianIndices(indsAt) IR = Broadcast.newindex(IA, keep, Idefault) for i in axes(A, 1) k, kss = y::Tuple - tmpAv = f(A[i,IA]) - tmpRv = Rval[i,IR] - tmpRi = Rind[i,IR] + tmpAv = f(@inbounds(A[i,IA])) + @inbounds tmpRv = Rval[i,IR] + @inbounds tmpRi = Rind[i,IR] if tmpRi == zi || op(tmpRv, tmpAv) - Rval[i,IR] = tmpAv - Rind[i,IR] = k + @inbounds Rval[i,IR] = tmpAv + @inbounds Rind[i,IR] = k end y = iterate(ks, kss) end diff --git a/base/reinterpretarray.jl b/base/reinterpretarray.jl index d74a043293a3a..d31f3ebb5dd2d 100644 --- a/base/reinterpretarray.jl +++ b/base/reinterpretarray.jl @@ -373,6 +373,7 @@ has_offset_axes(a::ReinterpretArray) = has_offset_axes(a.parent) elsize(::Type{<:ReinterpretArray{T}}) where {T} = sizeof(T) cconvert(::Type{Ptr{T}}, a::ReinterpretArray{T,N,S} where N) where {T,S} = cconvert(Ptr{S}, a.parent) +unsafe_convert(::Type{Ptr{T}}, a::ReinterpretArray{T,N,S} where N) where {T,S} = Ptr{T}(unsafe_convert(Ptr{S},a.parent)) @propagate_inbounds function getindex(a::NonReshapedReinterpretArray{T,0,S}) where {T,S} if isprimitivetype(T) && isprimitivetype(S) diff --git a/base/reshapedarray.jl b/base/reshapedarray.jl index c7ae1e3a25cd1..fb5a930bd1c32 100644 --- a/base/reshapedarray.jl +++ b/base/reshapedarray.jl @@ -307,6 +307,7 @@ setindex!(A::ReshapedRange, val, index::ReshapedIndex) = _rs_setindex!_err() @noinline _rs_setindex!_err() = error("indexed assignment fails for a reshaped range; consider calling collect") cconvert(::Type{Ptr{T}}, a::ReshapedArray{T}) where {T} = cconvert(Ptr{T}, parent(a)) +unsafe_convert(::Type{Ptr{T}}, a::ReshapedArray{T}) where {T} = unsafe_convert(Ptr{T}, a.parent) # Add a few handy specializations to further speed up views of reshaped ranges const ReshapedUnitRange{T,N,A<:AbstractUnitRange} = ReshapedArray{T,N,A,Tuple{}} diff --git a/base/show.jl b/base/show.jl index 217b22ab39ef8..13694df898def 100644 --- a/base/show.jl +++ b/base/show.jl @@ -1402,11 +1402,11 @@ function show_delim_array(io::IO, itr::Union{AbstractArray,SimpleVector}, op, de x = itr[i] show(recur_io, x) end - i += 1 - if i > l + if i == l delim_one && first && print(io, delim) break end + i += 1 first = false print(io, delim) print(io, ' ') diff --git a/base/strings/io.jl b/base/strings/io.jl index 3f8f531ae6ec4..97cdedac3db78 100644 --- a/base/strings/io.jl +++ b/base/strings/io.jl @@ -354,12 +354,21 @@ function join(io::IO, iterator, delim="") end function _join_preserve_annotations(iterator, args...) - if _isannotated(eltype(iterator)) || any(_isannotated, args) + et = @default_eltype(iterator) + if isconcretetype(et) && !_isannotated(et) && !any(_isannotated, args) + sprint(join, iterator, args...) + else io = AnnotatedIOBuffer() join(io, iterator, args...) - read(seekstart(io), AnnotatedString{String}) - else - sprint(join, iterator, args...) + # If we know (from compile time information, or dynamically in the case + # of iterators with a non-concrete eltype), that the result is annotated + # in nature, we extract an `AnnotatedString`, otherwise we just extract + # a plain `String` from `io`. + if isconcretetype(et) || !isempty(io.annotations) + read(seekstart(io), AnnotatedString{String}) + else + String(take!(io.io)) + end end end diff --git a/base/terminfo.jl b/base/terminfo.jl index 6f1d1ca8015f0..79713f4a86aa3 100644 --- a/base/terminfo.jl +++ b/base/terminfo.jl @@ -245,7 +245,8 @@ end Locate the terminfo file for `term`, return `nothing` if none could be found. The lookup policy is described in `terminfo(5)` "Fetching Compiled -Descriptions". +Descriptions". A terminfo database is included by default with Julia and is +taken to be the first entry of `@TERMINFO_DIRS@`. """ function find_terminfo_file(term::String) isempty(term) && return @@ -261,6 +262,7 @@ function find_terminfo_file(term::String) append!(terminfo_dirs, replace(split(ENV["TERMINFO_DIRS"], ':'), "" => "/usr/share/terminfo")) + push!(terminfo_dirs, normpath(Sys.BINDIR, DATAROOTDIR, "terminfo")) Sys.isunix() && push!(terminfo_dirs, "/etc/terminfo", "/lib/terminfo", "/usr/share/terminfo") for dir in terminfo_dirs @@ -268,8 +270,15 @@ function find_terminfo_file(term::String) return joinpath(dir, chr, term) elseif isfile(joinpath(dir, chrcode, term)) return joinpath(dir, chrcode, term) + elseif isfile(joinpath(dir, lowercase(chr), lowercase(term))) + # The vendored terminfo database is fully lowercase to avoid issues on + # case-sensitive filesystems. On Unix-like systems, terminfo files with + # different cases are hard links to one another, so this is still + # correct for non-vendored terminfo, just redundant. + return joinpath(dir, lowercase(chr), lowercase(term)) end end + return nothing end """ diff --git a/base/toml_parser.jl b/base/toml_parser.jl index 1a72e4e7d4bd2..7333875ef7169 100644 --- a/base/toml_parser.jl +++ b/base/toml_parser.jl @@ -38,7 +38,7 @@ const TOMLDict = Dict{String, Any} # Parser # ########## -mutable struct Parser +mutable struct Parser{Dates} str::String # 1 character look ahead current_char::Char @@ -86,12 +86,12 @@ mutable struct Parser filepath::Union{String, Nothing} # Optionally populate with the Dates stdlib to change the type of Date types returned - Dates::Union{Module, Nothing} + Dates::Union{Module, Nothing} # TODO: remove once Pkg is updated end -function Parser(str::String; filepath=nothing) +function Parser{Dates}(str::String; filepath=nothing) where {Dates} root = TOMLDict() - l = Parser( + l = Parser{Dates}( str, # str EOF_CHAR, # current_char firstindex(str), # pos @@ -112,6 +112,7 @@ function Parser(str::String; filepath=nothing) startup(l) return l end + function startup(l::Parser) # Populate our one character look-ahead c = eat_char(l) @@ -122,8 +123,10 @@ function startup(l::Parser) end end -Parser() = Parser("") -Parser(io::IO) = Parser(read(io, String)) +Parser{Dates}() where {Dates} = Parser{Dates}("") +Parser{Dates}(io::IO) where {Dates} = Parser{Dates}(read(io, String)) + +# Parser(...) will be defined by TOML stdlib function reinit!(p::Parser, str::String; filepath::Union{Nothing, String}=nothing) p.str = str @@ -1021,11 +1024,11 @@ function parse_datetime(l) return try_return_datetime(l, year, month, day, h, m, s, ms) end -function try_return_datetime(p, year, month, day, h, m, s, ms) - Dates = p.Dates - if Dates !== nothing +function try_return_datetime(p::Parser{Dates}, year, month, day, h, m, s, ms) where Dates + if Dates !== nothing || p.Dates !== nothing + mod = Dates !== nothing ? Dates : p.Dates try - return Dates.DateTime(year, month, day, h, m, s, ms) + return mod.DateTime(year, month, day, h, m, s, ms) catch ex ex isa ArgumentError && return ParserError(ErrParsingDateTime) rethrow() @@ -1035,11 +1038,11 @@ function try_return_datetime(p, year, month, day, h, m, s, ms) end end -function try_return_date(p, year, month, day) - Dates = p.Dates - if Dates !== nothing +function try_return_date(p::Parser{Dates}, year, month, day) where Dates + if Dates !== nothing || p.Dates !== nothing + mod = Dates !== nothing ? Dates : p.Dates try - return Dates.Date(year, month, day) + return mod.Date(year, month, day) catch ex ex isa ArgumentError && return ParserError(ErrParsingDateTime) rethrow() @@ -1058,11 +1061,11 @@ function parse_local_time(l::Parser) return try_return_time(l, h, m, s, ms) end -function try_return_time(p, h, m, s, ms) - Dates = p.Dates - if Dates !== nothing +function try_return_time(p::Parser{Dates}, h, m, s, ms) where Dates + if Dates !== nothing || p.Dates !== nothing + mod = Dates !== nothing ? Dates : p.Dates try - return Dates.Time(h, m, s, ms) + return mod.Time(h, m, s, ms) catch ex ex isa ArgumentError && return ParserError(ErrParsingDateTime) rethrow() diff --git a/cli/Makefile b/cli/Makefile index 8b46067820d85..feff949c2874b 100644 --- a/cli/Makefile +++ b/cli/Makefile @@ -150,7 +150,7 @@ $(build_bindir)/julia$(EXE): $(EXE_OBJS) $(build_shlibdir)/libjulia.$(SHLIB_EXT) $(build_bindir)/julia-debug$(EXE): $(EXE_DOBJS) $(build_shlibdir)/libjulia-debug.$(SHLIB_EXT) | $(build_bindir) @$(call PRINT_LINK, $(CC) $(LOADER_CFLAGS) $(DEBUGFLAGS) $(EXE_DOBJS) -o $@ $(LOADER_LDFLAGS) $(RPATH) -ljulia-debug) -$(BUILDDIR)/julia.expmap: $(SRCDIR)/julia.expmap.in +$(BUILDDIR)/julia.expmap: $(SRCDIR)/julia.expmap.in $(JULIAHOME)/VERSION sed <'$<' >'$@' -e 's/@JULIA_SHLIB_SYMBOL_VERSION@/JL_LIBJULIA_$(SOMAJOR)/' clean: | $(CLEAN_TARGETS) diff --git a/deps/Makefile b/deps/Makefile index 27f5fdbb693d5..6a1b03eec919e 100644 --- a/deps/Makefile +++ b/deps/Makefile @@ -170,6 +170,10 @@ DEP_LIBS += ittapi endif +ifneq ($(WITH_TERMINFO),0) +DEP_LIBS += terminfo +endif + # Only compile standalone LAPACK if we are not using OpenBLAS. # OpenBLAS otherwise compiles LAPACK as part of its build. # This is useful where one wants to use the vendor BLAS, but @@ -192,7 +196,7 @@ DEP_LIBS_STAGED := $(DEP_LIBS) DEP_LIBS_STAGED_ALL := llvm llvm-tools clang llvmunwind unwind libuv pcre \ openlibm dsfmt blastrampoline openblas lapack gmp mpfr patchelf utf8proc \ objconv mbedtls libssh2 nghttp2 curl libgit2 libwhich zlib p7zip csl \ - sanitizers libsuitesparse lld libtracyclient ittapi JuliaSyntax + sanitizers libsuitesparse lld libtracyclient ittapi JuliaSyntax terminfo DEP_LIBS_ALL := $(DEP_LIBS_STAGED_ALL) ifneq ($(USE_BINARYBUILDER_OPENBLAS),0) @@ -252,6 +256,7 @@ include $(SRCDIR)/libgit2.mk include $(SRCDIR)/libwhich.mk include $(SRCDIR)/p7zip.mk include $(SRCDIR)/libtracyclient.mk +include $(SRCDIR)/terminfo.mk # vendored Julia libs include $(SRCDIR)/JuliaSyntax.mk diff --git a/deps/blastrampoline.version b/deps/blastrampoline.version index 871053db3c9f2..fd055e1ae8120 100644 --- a/deps/blastrampoline.version +++ b/deps/blastrampoline.version @@ -2,6 +2,6 @@ BLASTRAMPOLINE_JLL_NAME := libblastrampoline ## source build -BLASTRAMPOLINE_VER := 5.10.1 -BLASTRAMPOLINE_BRANCH=v5.10.1 -BLASTRAMPOLINE_SHA1=ff05ebb4e450deda0aebe8dce4d4f054e23fecfc +BLASTRAMPOLINE_VER := 5.11.0 +BLASTRAMPOLINE_BRANCH=v5.11.0 +BLASTRAMPOLINE_SHA1=05083d50611b5538df69706f0a952d8e642b0b4b diff --git a/deps/checksums/Pkg-2ff691035e9b2780cbd2fcb8dd30b640d85edde2.tar.gz/md5 b/deps/checksums/Pkg-2ff691035e9b2780cbd2fcb8dd30b640d85edde2.tar.gz/md5 new file mode 100644 index 0000000000000..fe1a6a5d418b4 --- /dev/null +++ b/deps/checksums/Pkg-2ff691035e9b2780cbd2fcb8dd30b640d85edde2.tar.gz/md5 @@ -0,0 +1 @@ +6f6793bcb797bfd2c48f52c631ce9479 diff --git a/deps/checksums/Pkg-2ff691035e9b2780cbd2fcb8dd30b640d85edde2.tar.gz/sha512 b/deps/checksums/Pkg-2ff691035e9b2780cbd2fcb8dd30b640d85edde2.tar.gz/sha512 new file mode 100644 index 0000000000000..ea7ace0f5e812 --- /dev/null +++ b/deps/checksums/Pkg-2ff691035e9b2780cbd2fcb8dd30b640d85edde2.tar.gz/sha512 @@ -0,0 +1 @@ +70a9e47694082ab3f999891e03a932d1d43f9a700de1c7943d943f11c98d0893dafa9ed19f585dfe1af9aa053c5db1efde4af55c26b824632afa057ecb97f07a diff --git a/deps/checksums/Pkg-8457d3eff518f75bbff2d8b183e66e44ae5c47f1.tar.gz/md5 b/deps/checksums/Pkg-8457d3eff518f75bbff2d8b183e66e44ae5c47f1.tar.gz/md5 deleted file mode 100644 index d7b567d238603..0000000000000 --- a/deps/checksums/Pkg-8457d3eff518f75bbff2d8b183e66e44ae5c47f1.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -493a4e2e5f8f0e9a0f7b212d9090c4d3 diff --git a/deps/checksums/Pkg-8457d3eff518f75bbff2d8b183e66e44ae5c47f1.tar.gz/sha512 b/deps/checksums/Pkg-8457d3eff518f75bbff2d8b183e66e44ae5c47f1.tar.gz/sha512 deleted file mode 100644 index 3219baba7b6ef..0000000000000 --- a/deps/checksums/Pkg-8457d3eff518f75bbff2d8b183e66e44ae5c47f1.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -4940684b63729357560aec6268049142b89fe36fab9426d978f486340f2de33abfb2a2b5b7f3692c973e303527b685dda42f16e634cf57549dced7889e3a6ff8 diff --git a/deps/checksums/StyledStrings-ac472083359dde956aed8c61d43b8158ac84d9ce.tar.gz/md5 b/deps/checksums/StyledStrings-ac472083359dde956aed8c61d43b8158ac84d9ce.tar.gz/md5 deleted file mode 100644 index 758a74bce9dae..0000000000000 --- a/deps/checksums/StyledStrings-ac472083359dde956aed8c61d43b8158ac84d9ce.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -6969fb6d2e8585d26beef865910ec8ef diff --git a/deps/checksums/StyledStrings-ac472083359dde956aed8c61d43b8158ac84d9ce.tar.gz/sha512 b/deps/checksums/StyledStrings-ac472083359dde956aed8c61d43b8158ac84d9ce.tar.gz/sha512 deleted file mode 100644 index 3d1ac8791e14d..0000000000000 --- a/deps/checksums/StyledStrings-ac472083359dde956aed8c61d43b8158ac84d9ce.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -281292e8478d72ab66b84cbd4f42e5dc2dd5054e8c54a79de8f0c0537d28962b460e67fe71230ead6b02386b87d0423879d51ce53a2b2427ce55866d62d6ebde diff --git a/deps/checksums/StyledStrings-f6035eb97b516862b16e36cab2ecc6ea8adc3d7c.tar.gz/md5 b/deps/checksums/StyledStrings-f6035eb97b516862b16e36cab2ecc6ea8adc3d7c.tar.gz/md5 new file mode 100644 index 0000000000000..0d39747d275ba --- /dev/null +++ b/deps/checksums/StyledStrings-f6035eb97b516862b16e36cab2ecc6ea8adc3d7c.tar.gz/md5 @@ -0,0 +1 @@ +bf7c157df6084942b794fbe5b768a643 diff --git a/deps/checksums/StyledStrings-f6035eb97b516862b16e36cab2ecc6ea8adc3d7c.tar.gz/sha512 b/deps/checksums/StyledStrings-f6035eb97b516862b16e36cab2ecc6ea8adc3d7c.tar.gz/sha512 new file mode 100644 index 0000000000000..d0a8d6cec08cf --- /dev/null +++ b/deps/checksums/StyledStrings-f6035eb97b516862b16e36cab2ecc6ea8adc3d7c.tar.gz/sha512 @@ -0,0 +1 @@ +ba2f6b91494662208842dec580ea9410d8d6ba4e57315c72e872227f5e2f68cc970fcf5dbd9c8a03920f93b6adabdeaab738fff04f9ca7b5da5cd6b89759e7f6 diff --git a/deps/checksums/blastrampoline b/deps/checksums/blastrampoline index b8c4c68c661ba..edb8cadc74846 100644 --- a/deps/checksums/blastrampoline +++ b/deps/checksums/blastrampoline @@ -1,34 +1,34 @@ -blastrampoline-ff05ebb4e450deda0aebe8dce4d4f054e23fecfc.tar.gz/md5/48ec847f7a687dd36789d6365d3c5645 -blastrampoline-ff05ebb4e450deda0aebe8dce4d4f054e23fecfc.tar.gz/sha512/85f6a46e7fe5f76ff8cef5776dad73b17eb97be3b16ca1af961cf2c2cbe125c629bd808b0243b793e4235dcb545a02cc082eaf14b3a438f3e0973d46921550a3 -libblastrampoline.v5.10.1+0.aarch64-apple-darwin.tar.gz/md5/cbbb4b5a6ebee04d686f072a69e855be -libblastrampoline.v5.10.1+0.aarch64-apple-darwin.tar.gz/sha512/32eaebb0fa3c0bc85a270b5c13fecaaa86ee10b4cea04405672badbaaa5ae3f22757dc758d9d971c811dc100a8ebd72fa00391238c0227de3690341f0434842a -libblastrampoline.v5.10.1+0.aarch64-linux-gnu.tar.gz/md5/da097a9459dcb8554f3d9511ea1a1c88 -libblastrampoline.v5.10.1+0.aarch64-linux-gnu.tar.gz/sha512/0159dbd4579d2a334f4341a64841bc1cef1354fc744709055339957b299b2b36b30162c2c90367abc04a2fb2f236aaa1fe6eb290393702f6fb97eaa79e4bb028 -libblastrampoline.v5.10.1+0.aarch64-linux-musl.tar.gz/md5/f32839481836dad6a1b159d9c33db752 -libblastrampoline.v5.10.1+0.aarch64-linux-musl.tar.gz/sha512/b973e739ab4af6ba93328943b03f16f02625553efc2375909b5e5bed4446287a21f99025817ce73267cac2d0b6b65f7dc2a5bd4b4c88d263b3c923b2ec3ad5c4 -libblastrampoline.v5.10.1+0.armv6l-linux-gnueabihf.tar.gz/md5/23eb2cbc1a547f94935fa4f9ffa2285b -libblastrampoline.v5.10.1+0.armv6l-linux-gnueabihf.tar.gz/sha512/0681497bac1d8f3ff1932adbb9fdd0b710b2a28ca7f2f4bb0093ba1123b14acd8bcb062e81e538c6e51ed8449ffea582cdb5b610e97d0c76a6feb58545938a6b -libblastrampoline.v5.10.1+0.armv6l-linux-musleabihf.tar.gz/md5/4e5168b1ada4e36861aeb3f4a6ace318 -libblastrampoline.v5.10.1+0.armv6l-linux-musleabihf.tar.gz/sha512/4ee663d2d3665e6ea356cfac60274c5f06ab08c1ee99b345ddda6872125663acb5559f704d0a918706e6cb075fc3071aaec4bcc3b9fee5fee72696e2f1454fb3 -libblastrampoline.v5.10.1+0.armv7l-linux-gnueabihf.tar.gz/md5/a28e3820fdf1435027f69204a553b5f9 -libblastrampoline.v5.10.1+0.armv7l-linux-gnueabihf.tar.gz/sha512/48edfc069aeaead468ffb6145986b11a040286990520b191e0f9cfa99f0b9458e6b17e523c8cc81889af7c9c2adce6372c65f2205a20c8e778614eaa06d288f9 -libblastrampoline.v5.10.1+0.armv7l-linux-musleabihf.tar.gz/md5/c5ea1756f3d58f8a74e76958f3a93658 -libblastrampoline.v5.10.1+0.armv7l-linux-musleabihf.tar.gz/sha512/f3eb003f954ffc346ae1037325b56fb2e4db9a6f88cc878862f921df79d8e0a5c8da9d229610dcd5d21c3d7af0a61ddcc0e70e32bf45fc9ea828d9ab2d1ddda8 -libblastrampoline.v5.10.1+0.i686-linux-gnu.tar.gz/md5/8bbdd602fed40577c4c9f020a8304c57 -libblastrampoline.v5.10.1+0.i686-linux-gnu.tar.gz/sha512/67947bd68c9f1131311d5d6a0fbcc92540f2fb2e1d2d0fa46951033fd75658661ba355c415b68de5dcd1bf0c440e27e3362ece70f5fd989fade796e9e723becd -libblastrampoline.v5.10.1+0.i686-linux-musl.tar.gz/md5/455bb539e7646e060fa24fb59c82f2f0 -libblastrampoline.v5.10.1+0.i686-linux-musl.tar.gz/sha512/e392334512ebce93ea4b34265ead802c543db5678c30083fb0dce08c071dd7140a9532d3162f215815807650138ffec5ad5d6d848025ee3913dfe353308d8e57 -libblastrampoline.v5.10.1+0.i686-w64-mingw32.tar.gz/md5/9a1c6845cb2e85b3497cd01d3a89b06b -libblastrampoline.v5.10.1+0.i686-w64-mingw32.tar.gz/sha512/66a9429a70575f4fd19d1cfb263c4c7801ac4a88408f98125f6e347b0ba35d2fdc4cbb82bf7407462beab1f7a7df2184163f76d5f2330f485bc1c7e5354716aa -libblastrampoline.v5.10.1+0.powerpc64le-linux-gnu.tar.gz/md5/b2b3eea1cfce87642a1f2afa125dcc5c -libblastrampoline.v5.10.1+0.powerpc64le-linux-gnu.tar.gz/sha512/43d5bf6535ad8f0910a523a3940787db956a3700681cc0dc1e2a1aabdaafa669e46e42854df29c0dcff06b3ade899159cb4845a48a6e618ba52af7276151fd0e -libblastrampoline.v5.10.1+0.x86_64-apple-darwin.tar.gz/md5/497a8f88c810a12b3faf12851427c784 -libblastrampoline.v5.10.1+0.x86_64-apple-darwin.tar.gz/sha512/7e3ed2117c6248761ba5bc3fd339f12ca98050d163d5c3668a62ee90aec10858d30fe9d78cea01796c9b2231cdd4f9ad0ae886bf8e984cb24d745e9f8c0fd62b -libblastrampoline.v5.10.1+0.x86_64-linux-gnu.tar.gz/md5/355612dc7c383dd860dc03498254814b -libblastrampoline.v5.10.1+0.x86_64-linux-gnu.tar.gz/sha512/12d803c53f705dacf2bf5f3884bd9b40f89a248ebda8bce1da6bba0cfe4331222bed5124dc45ea377e7c0fcc2d0dc624cc71b0eb454319fd12e2fd4c58d265f7 -libblastrampoline.v5.10.1+0.x86_64-linux-musl.tar.gz/md5/78a09fe918b1b0b3dc72c17c2e62799b -libblastrampoline.v5.10.1+0.x86_64-linux-musl.tar.gz/sha512/1ff3d7e8d36d450f430119b30e03a64f2d78d6d13a04e4a4b97c64966e341f486080c733dbd73ee3ed7c1557ad737f37c013335578e1555d162f0591929de747 -libblastrampoline.v5.10.1+0.x86_64-unknown-freebsd.tar.gz/md5/ad9f213bc4a7882784ad09017fc82234 -libblastrampoline.v5.10.1+0.x86_64-unknown-freebsd.tar.gz/sha512/4de6f08a45cb3c3819f71ccd44688b847c2e9b36e0d4bce94191558fe2d775c2790f4c68eea1a366d0a869f0c986aa33626d427946403cf4e128f45b5881f70e -libblastrampoline.v5.10.1+0.x86_64-w64-mingw32.tar.gz/md5/2d0cf117d8d797e7716f8d836dfdd9f5 -libblastrampoline.v5.10.1+0.x86_64-w64-mingw32.tar.gz/sha512/d7a94f3a71400b22b6c14648455e38dff750eb88661928b66b307f721d53769dea3aec43bb86e2200145ed072475c32e1bfc38e0fc35445c4c42e5752754b0e5 +blastrampoline-05083d50611b5538df69706f0a952d8e642b0b4b.tar.gz/md5/700b22cb26291736bd1263cd2a7f2d75 +blastrampoline-05083d50611b5538df69706f0a952d8e642b0b4b.tar.gz/sha512/967c16d28834df112916c0904dd4c7231a1c5e4edf279adb26411faa17da28eee4680ce2347b3941520dccbc768944277a8f724b21976960d00f840349b90e36 +libblastrampoline.v5.11.0+0.aarch64-apple-darwin.tar.gz/md5/769458d40e004d6126cae6b34351068f +libblastrampoline.v5.11.0+0.aarch64-apple-darwin.tar.gz/sha512/75a726b9a4f41b70344ceb9e1f1a7ad370bfa84ce44c70b8a965061d777871e3bf2237ae055da7e6202ddef78932ba8baf2a01a675b1b0cec5338ef16ea2081b +libblastrampoline.v5.11.0+0.aarch64-linux-gnu.tar.gz/md5/d92cf3f3fa1e977ea3a1a74acc8442d1 +libblastrampoline.v5.11.0+0.aarch64-linux-gnu.tar.gz/sha512/3354f4eec2a410f81cc0546a04ce98ddd416d441c1701a59ec5bebea99af8823b5af10a85cb4e3377548422c6d6a0a870f2e7a05ad0cda52c6143361d59ba4fb +libblastrampoline.v5.11.0+0.aarch64-linux-musl.tar.gz/md5/41d060c03202b662e47bda5fbf7b1e84 +libblastrampoline.v5.11.0+0.aarch64-linux-musl.tar.gz/sha512/54a05516e12350441c33341fde53bc912aa52dc4b746089c2d21cb75f24f0fb140849a520327db6f52895743eab090b59fa974a2a426a49f8b4e38693340a306 +libblastrampoline.v5.11.0+0.armv6l-linux-gnueabihf.tar.gz/md5/4930dceefac63e7aa5a93e1ba0e00e59 +libblastrampoline.v5.11.0+0.armv6l-linux-gnueabihf.tar.gz/sha512/dafce083c2b409ead61fdbdf4f46b7c93cab00c82a74a181d381c4a93f1e7af035cd6caf407b0199c1f8c2f2f68f93d67938ef092fa4a8d1133f0ea73fb51a9c +libblastrampoline.v5.11.0+0.armv6l-linux-musleabihf.tar.gz/md5/82346cc4ddeaa29ea7a081edfdfcb08b +libblastrampoline.v5.11.0+0.armv6l-linux-musleabihf.tar.gz/sha512/72e387bd661096a46077e8c15e12f8a6f18fd6aaf30af0678d00eca0d83af10758874643f5716539dd38269e831e4649d45db739aeb60996bf1b96277cea1d17 +libblastrampoline.v5.11.0+0.armv7l-linux-gnueabihf.tar.gz/md5/7e8f115268e8c62acaa2a53ecd32e2fe +libblastrampoline.v5.11.0+0.armv7l-linux-gnueabihf.tar.gz/sha512/4210c306ff7ccb53aa6c9f45e134c63b238c563ed753f7536dfc21f6962dfea35d9de62e429e2685b70d0db780ac766b72fd5e76e2d62df74000e3e5d553c30f +libblastrampoline.v5.11.0+0.armv7l-linux-musleabihf.tar.gz/md5/7f388611c477b528a091f697b0d334d9 +libblastrampoline.v5.11.0+0.armv7l-linux-musleabihf.tar.gz/sha512/e9b017dfa8c19cb940395b253f3b28511a6619469fabff7ab1671ed0936e9e0681d1385c3d1f5d6417ccb65ffbdcf53a0c8519d4ef8e89f9500a05ca00296144 +libblastrampoline.v5.11.0+0.i686-linux-gnu.tar.gz/md5/254948ea87a435251b1e064a77b3d635 +libblastrampoline.v5.11.0+0.i686-linux-gnu.tar.gz/sha512/5a51d3c20c49c497a8f0c2d2e7b38b49ec5e367c7013a7f0efa4fc099639da20ef9c0bfdbdfbdc40b27ce61f189b18f5cf617d7a0ed4bc5300da692f7d6b77a4 +libblastrampoline.v5.11.0+0.i686-linux-musl.tar.gz/md5/a9504870af8db1e247be02c5e188f7a5 +libblastrampoline.v5.11.0+0.i686-linux-musl.tar.gz/sha512/5f0109168a16edb8ca66fcf10c2c10b57fe9c3061c0b08dac4dea936538fa5854aa1b66079f127b5d9902288b61772054013256aa307b682de38e350b1bbb367 +libblastrampoline.v5.11.0+0.i686-w64-mingw32.tar.gz/md5/815822f6bacb42c35b80bc77458c5c49 +libblastrampoline.v5.11.0+0.i686-w64-mingw32.tar.gz/sha512/c82f8c6fe0b7917860e5601c79e35d56297c53b6f7f992841d4f048e7981533e459f9db0805a16d82a9e03d452489760def0d9c57181dcfa5dc363102180eecd +libblastrampoline.v5.11.0+0.powerpc64le-linux-gnu.tar.gz/md5/ee30c9cb4c51df03026f9e471040e9cc +libblastrampoline.v5.11.0+0.powerpc64le-linux-gnu.tar.gz/sha512/5055d83a1b0625364ddd97652a4c6fa39c795078123cad33a085283889274f66c9dc053be0591c14be262dc7eef666726afa922c66ae8d05c2791c3d6bd7009e +libblastrampoline.v5.11.0+0.x86_64-apple-darwin.tar.gz/md5/210cd354c9b4a8aa2a2b55723597e58b +libblastrampoline.v5.11.0+0.x86_64-apple-darwin.tar.gz/sha512/1ee65d598f9f8a2cf7137135c8c2c431520b1cde319fc33dddfbdae9fe01d986e979a97c24cf85c090cc40064cfe47c376dfeb088ff417d17868c4df84fb2fd4 +libblastrampoline.v5.11.0+0.x86_64-linux-gnu.tar.gz/md5/e2213c42eebee6e45079ef6831077b3f +libblastrampoline.v5.11.0+0.x86_64-linux-gnu.tar.gz/sha512/ab2c3026d34962a2ca5116d71a4e8eaaca5182d53f21edd3e4be81ce26e74e427c88797308af7fbbf1b9ee615e0383acf0dae1d0eb207ebc64dddaf927f00b48 +libblastrampoline.v5.11.0+0.x86_64-linux-musl.tar.gz/md5/8cde3c618e882ea2b7c8a017a69175c7 +libblastrampoline.v5.11.0+0.x86_64-linux-musl.tar.gz/sha512/8a3aca5691c3936d114c804471b2429b9ae81308f020247765614d2f792f93a012263ce4baa31cf42f4dacc23a7161a4c7f9debfba8d9028879f1ed3fc4e2433 +libblastrampoline.v5.11.0+0.x86_64-unknown-freebsd.tar.gz/md5/b02eb694e1486ef8ffe9534ac2bd5ec6 +libblastrampoline.v5.11.0+0.x86_64-unknown-freebsd.tar.gz/sha512/989273809ae567d7e7193529740423ac1870eae3a0effeecc67f84da914d81649786f393e101f013b7232ef5fe35066d89b3cb776ad0e87394799491ef28a467 +libblastrampoline.v5.11.0+0.x86_64-w64-mingw32.tar.gz/md5/6e7f602ab0bf5a5c28bf4e959a1bbf77 +libblastrampoline.v5.11.0+0.x86_64-w64-mingw32.tar.gz/sha512/556e7ca1a2576c1d7825ac1bc2449ffe2cd40391cf316b10f60681a5c736939c97eb5221c2837640928b5544f89f44cb14ca44ccf54092376390ea1a6012c9e5 diff --git a/deps/checksums/terminfo b/deps/checksums/terminfo new file mode 100644 index 0000000000000..bd971e72b1be8 --- /dev/null +++ b/deps/checksums/terminfo @@ -0,0 +1,2 @@ +TermInfoDB-v2023.12.9.any.tar.gz/md5/573d9b5adaf6af500e3dfae6e3d15ebf +TermInfoDB-v2023.12.9.any.tar.gz/sha512/e0a5bfe54346f9d5690a840628b329f6fac7375b0d29337bc70813ae3553a72bb397f8034d221c544289e40c4cfc685d5805777b7528f05bbe0123b5905c24a4 diff --git a/deps/llvm.mk b/deps/llvm.mk index 3e3ea4e79c24e..36d6e8547783d 100644 --- a/deps/llvm.mk +++ b/deps/llvm.mk @@ -93,15 +93,14 @@ LLVM_LDFLAGS += $(LDFLAGS) LLVM_CMAKE += -DLLVM_TARGETS_TO_BUILD:STRING="$(LLVM_TARGETS)" -DCMAKE_BUILD_TYPE="$(LLVM_CMAKE_BUILDTYPE)" LLVM_CMAKE += -DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD:STRING="$(LLVM_EXPERIMENTAL_TARGETS)" LLVM_CMAKE += -DLLVM_ENABLE_LIBXML2=OFF -DLLVM_HOST_TRIPLE="$(or $(XC_HOST),$(BUILD_MACHINE))" -LLVM_CMAKE += -DLLVM_ENABLE_ZLIB=ON -DZLIB_LIBRARY="$(build_prefix)/lib" -LLVM_CMAKE += -DCOMPILER_RT_ENABLE_IOS=OFF -DCOMPILER_RT_ENABLE_WATCHOS=OFF -DCOMPILER_RT_ENABLE_TVOS=OFF +LLVM_CMAKE += -DLLVM_ENABLE_ZLIB=FORCE_ON -DZLIB_ROOT="$(build_prefix)" ifeq ($(USE_POLLY_ACC),1) LLVM_CMAKE += -DPOLLY_ENABLE_GPGPU_CODEGEN=ON endif LLVM_CMAKE += -DLLVM_TOOLS_INSTALL_DIR=$(call rel_path,$(build_prefix),$(build_depsbindir)) LLVM_CMAKE += -DLLVM_UTILS_INSTALL_DIR=$(call rel_path,$(build_prefix),$(build_depsbindir)) LLVM_CMAKE += -DLLVM_INCLUDE_UTILS=ON -DLLVM_INSTALL_UTILS=ON -LLVM_CMAKE += -DLLVM_BINDINGS_LIST="" -DLLVM_ENABLE_BINDINGS=OFF -DLLVM_INCLUDE_DOCS=Off -DLLVM_ENABLE_TERMINFO=Off -DHAVE_HISTEDIT_H=Off -DHAVE_LIBEDIT=Off +LLVM_CMAKE += -DLLVM_BINDINGS_LIST="" -DLLVM_ENABLE_BINDINGS=OFF -DLLVM_INCLUDE_DOCS=Off -DLLVM_ENABLE_TERMINFO=Off -DHAVE_LIBEDIT=Off ifeq ($(LLVM_ASSERTIONS), 1) LLVM_CMAKE += -DLLVM_ENABLE_ASSERTIONS:BOOL=ON endif # LLVM_ASSERTIONS diff --git a/deps/terminfo.mk b/deps/terminfo.mk new file mode 100644 index 0000000000000..63194f786f566 --- /dev/null +++ b/deps/terminfo.mk @@ -0,0 +1,43 @@ +## TERMINFO-DB ## +include $(SRCDIR)/terminfo.version + +$(SRCCACHE)/TermInfoDB-v$(TERMINFO_VER).any.tar.gz: | $(SRCCACHE) + $(JLDOWNLOAD) $@ https://github.com/JuliaBinaryWrappers/TermInfoDB_jll.jl/releases/download/$(TERMINFO_TAG)/TermInfoDB.v$(TERMINFO_VER).any.tar.gz + touch -c $@ + +$(BUILDDIR)/TermInfoDB-v$(TERMINFO_VER)/source-extracted: $(SRCCACHE)/TermInfoDB-v$(TERMINFO_VER).any.tar.gz + $(JLCHECKSUM) $< + rm -rf $(dir $@) + mkdir -p $(dir $@) + $(TAR) -C $(dir $@) --strip-components 1 -xf $< + echo 1 > $@ + +checksum-terminfo: $(SRCCACHE)/TermInfoDB-v$(TERMINFO_VER).any.tar.gz + $(JLCHECKSUM) $< + +$(BUILDDIR)/TermInfoDB-v$(TERMINFO_VER)/build-compiled: $(BUILDDIR)/TermInfoDB-v$(TERMINFO_VER)/source-extracted + echo 1 > $@ + +$(BUILDDIR)/TermInfoDB-v$(TERMINFO_VER)/build-checked: $(BUILDDIR)/TermInfoDB-v$(TERMINFO_VER)/build-compiled + echo 1 > $@ + +define TERMINFO_INSTALL + mkdir -p $2/$$(build_datarootdir) + cp -R $1/terminfo $2/$$(build_datarootdir) +endef +$(eval $(call staged-install, \ + terminfo,TermInfoDB-v$(TERMINFO_VER), \ + TERMINFO_INSTALL,,,,)) + +clean-terminfo: + -rm -f $(BUILDDIR)/TermInfoDB-v$(TERMINFO_VER)/build-compiled + +distclean-terminfo: + rm -rf $(SRCCACHE)/TermInfoDB*.tar.gz $(SRCCACHE)/TermInfoDB-v$(TERMINFO_VER) $(BUILDDIR)/TermInfoDB-v$(TERMINFO_VER) + +get-terminfo: $(SRCCACHE)/TermInfoDB-v$(TERMINFO_VER).any.tar.gz +extract-terminfo: $(BUILDDIR)/TermInfoDB-v$(TERMINFO_VER)/source-extracted +configure-terminfo: extract-terminfo +compile-terminfo: $(BUILDDIR)/TermInfoDB-v$(TERMINFO_VER)/build-compiled +fastcheck-terminfo: check-terminfo +check-terminfo: $(BUILDDIR)/TermInfoDB-v$(TERMINFO_VER)/build-checked diff --git a/deps/terminfo.version b/deps/terminfo.version new file mode 100644 index 0000000000000..b7c020b830517 --- /dev/null +++ b/deps/terminfo.version @@ -0,0 +1,3 @@ +# -*- makefile -*- +TERMINFO_VER := 2023.12.9 +TERMINFO_TAG := TermInfoDB-v$(TERMINFO_VER)+0 diff --git a/doc/src/base/base.md b/doc/src/base/base.md index 739ee97d1dd43..29ecba80bf14c 100644 --- a/doc/src/base/base.md +++ b/doc/src/base/base.md @@ -307,7 +307,12 @@ Base.@simd Base.@polly Base.@generated Base.@assume_effects +``` + +## Managing deprecations +```@docs Base.@deprecate +Base.depwarn ``` ## Missing Values diff --git a/src/Makefile b/src/Makefile index f1c71c28a7265..9c48c04b25f9e 100644 --- a/src/Makefile +++ b/src/Makefile @@ -383,7 +383,7 @@ $(BUILDDIR)/julia_version.h: $(JULIAHOME)/VERSION CXXLD = $(CXX) -shared -$(BUILDDIR)/julia.expmap: $(SRCDIR)/julia.expmap.in +$(BUILDDIR)/julia.expmap: $(SRCDIR)/julia.expmap.in $(JULIAHOME)/VERSION $(LLVM_CONFIG_ABSOLUTE) sed <'$<' >'$@' -e "s/@JULIA_SHLIB_SYMBOL_VERSION@/JL_LIBJULIA_$(SOMAJOR)/" \ -e "s/@LLVM_SHLIB_SYMBOL_VERSION@/$(LLVM_SHLIB_SYMBOL_VERSION)/" diff --git a/src/builtins.c b/src/builtins.c index 361c1f1f4cf85..de2b067a773e1 100644 --- a/src/builtins.c +++ b/src/builtins.c @@ -2105,6 +2105,12 @@ static int references_name(jl_value_t *p, jl_typename_t *name, int affects_layou return references_name(((jl_uniontype_t*)p)->a, name, affects_layout, freevars) || references_name(((jl_uniontype_t*)p)->b, name, affects_layout, freevars); } + if (jl_is_vararg(p)) { + jl_value_t *T = ((jl_vararg_t*)p)->T; + jl_value_t *N = ((jl_vararg_t*)p)->N; + return (T && references_name(T, name, affects_layout, freevars)) || + (N && references_name(N, name, affects_layout, freevars)); + } if (jl_is_typevar(p)) return 0; // already checked by unionall, if applicable if (jl_is_datatype(p)) { diff --git a/src/ccall.cpp b/src/ccall.cpp index 96f0526fc8cf2..6d0330569c72a 100644 --- a/src/ccall.cpp +++ b/src/ccall.cpp @@ -551,8 +551,8 @@ static Value *julia_to_native( // pass the address of an alloca'd thing, not a box // since those are immutable. Value *slot = emit_static_alloca(ctx, to); - unsigned align = julia_alignment(jlto); - cast(slot)->setAlignment(Align(align)); + Align align(julia_alignment(jlto)); + cast(slot)->setAlignment(align); setName(ctx.emission_context, slot, "native_convert_buffer"); if (!jvinfo.ispointer()) { jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, jvinfo.tbaa); @@ -1558,9 +1558,8 @@ static jl_cgval_t emit_ccall(jl_codectx_t &ctx, jl_value_t **args, size_t nargs) emit_gc_safepoint(ctx.builder, ctx.types().T_size, get_current_ptls(ctx), ctx.tbaa().tbaa_const); return ghostValue(ctx, jl_nothing_type); } - else if (is_libjulia_func("jl_get_ptls_states")) { + else if (is_libjulia_func(jl_get_ptls_states)) { ++CCALL_STAT(jl_get_ptls_states); - assert(lrt == ctx.types().T_size); assert(!isVa && !llvmcall && nccallargs == 0); JL_GC_POP(); return mark_or_box_ccall_result(ctx, @@ -2092,7 +2091,7 @@ jl_cgval_t function_sig_t::emit_a_ccall( Value *strct = emit_allocobj(ctx, (jl_datatype_t*)rt, true); setName(ctx.emission_context, strct, "ccall_ret_box"); MDNode *tbaa = jl_is_mutable(rt) ? ctx.tbaa().tbaa_mutab : ctx.tbaa().tbaa_immut; - int boxalign = julia_alignment(rt); + Align boxalign(julia_alignment(rt)); // copy the data from the return value to the new struct const DataLayout &DL = ctx.builder.GetInsertBlock()->getModule()->getDataLayout(); auto resultTy = result->getType(); @@ -2102,8 +2101,8 @@ jl_cgval_t function_sig_t::emit_a_ccall( // When this happens, cast through memory. auto slot = emit_static_alloca(ctx, resultTy); setName(ctx.emission_context, slot, "type_pun_slot"); - slot->setAlignment(Align(boxalign)); - ctx.builder.CreateAlignedStore(result, slot, Align(boxalign)); + slot->setAlignment(boxalign); + ctx.builder.CreateAlignedStore(result, slot, boxalign); jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, tbaa); emit_memcpy(ctx, strct, ai, slot, ai, rtsz, boxalign, boxalign); } diff --git a/src/cgutils.cpp b/src/cgutils.cpp index 9fd7a368faf39..d049327c2bf36 100644 --- a/src/cgutils.cpp +++ b/src/cgutils.cpp @@ -310,7 +310,7 @@ static Value *emit_pointer_from_objref(jl_codectx_t &ctx, Value *V) } static Value *emit_unbox(jl_codectx_t &ctx, Type *to, const jl_cgval_t &x, jl_value_t *jt); -static void emit_unbox_store(jl_codectx_t &ctx, const jl_cgval_t &x, Value* dest, MDNode *tbaa_dest, unsigned alignment, bool isVolatile=false); +static void emit_unbox_store(jl_codectx_t &ctx, const jl_cgval_t &x, Value* dest, MDNode *tbaa_dest, Align alignment, bool isVolatile=false); static Value *get_gc_root_for(jl_codectx_t &ctx, const jl_cgval_t &x) { @@ -980,11 +980,10 @@ static Value *data_pointer(jl_codectx_t &ctx, const jl_cgval_t &x) } static void emit_memcpy_llvm(jl_codectx_t &ctx, Value *dst, jl_aliasinfo_t const &dst_ai, Value *src, - jl_aliasinfo_t const &src_ai, uint64_t sz, unsigned align_dst, unsigned align_src, bool is_volatile) + jl_aliasinfo_t const &src_ai, uint64_t sz, Align align_dst, Align align_src, bool is_volatile) { if (sz == 0) return; - assert(align_dst && "align must be specified"); // If the types are small and simple, use load and store directly. // Going through memcpy can cause LLVM (e.g. SROA) to create bitcasts between float and int // that interferes with other optimizations. @@ -1026,7 +1025,7 @@ static void emit_memcpy_llvm(jl_codectx_t &ctx, Value *dst, jl_aliasinfo_t const if (isa(dst) && !dst->hasName()) setName(ctx.emission_context, dst, "memcpy_refined_dst"); auto val = src_ai.decorateInst(ctx.builder.CreateAlignedLoad(directel, src, MaybeAlign(align_src), is_volatile)); - dst_ai.decorateInst(ctx.builder.CreateAlignedStore(val, dst, Align(align_dst), is_volatile)); + dst_ai.decorateInst(ctx.builder.CreateAlignedStore(val, dst, align_dst, is_volatile)); ++SkippedMemcpys; return; } @@ -1044,12 +1043,12 @@ static void emit_memcpy_llvm(jl_codectx_t &ctx, Value *dst, jl_aliasinfo_t const // above problem won't be as serious. auto merged_ai = dst_ai.merge(src_ai); - ctx.builder.CreateMemCpy(dst, Align(align_dst), src, Align(align_src), sz, is_volatile, + ctx.builder.CreateMemCpy(dst, align_dst, src, align_src, sz, is_volatile, merged_ai.tbaa, merged_ai.tbaa_struct, merged_ai.scope, merged_ai.noalias); } static void emit_memcpy_llvm(jl_codectx_t &ctx, Value *dst, jl_aliasinfo_t const &dst_ai, Value *src, - jl_aliasinfo_t const &src_ai, Value *sz, unsigned align_dst, unsigned align_src, bool is_volatile) + jl_aliasinfo_t const &src_ai, Value *sz, Align align_dst, Align align_src, bool is_volatile) { if (auto const_sz = dyn_cast(sz)) { emit_memcpy_llvm(ctx, dst, dst_ai, src, src_ai, const_sz->getZExtValue(), align_dst, align_src, is_volatile); @@ -1058,20 +1057,20 @@ static void emit_memcpy_llvm(jl_codectx_t &ctx, Value *dst, jl_aliasinfo_t const ++EmittedMemcpys; auto merged_ai = dst_ai.merge(src_ai); - ctx.builder.CreateMemCpy(dst, MaybeAlign(align_dst), src, MaybeAlign(align_src), sz, is_volatile, + ctx.builder.CreateMemCpy(dst, align_dst, src, align_src, sz, is_volatile, merged_ai.tbaa, merged_ai.tbaa_struct, merged_ai.scope, merged_ai.noalias); } template static void emit_memcpy(jl_codectx_t &ctx, Value *dst, jl_aliasinfo_t const &dst_ai, Value *src, - jl_aliasinfo_t const &src_ai, T1 &&sz, unsigned align_dst, unsigned align_src, bool is_volatile=false) + jl_aliasinfo_t const &src_ai, T1 &&sz, Align align_dst, Align align_src, bool is_volatile=false) { emit_memcpy_llvm(ctx, dst, dst_ai, src, src_ai, sz, align_dst, align_src, is_volatile); } template static void emit_memcpy(jl_codectx_t &ctx, Value *dst, jl_aliasinfo_t const &dst_ai, const jl_cgval_t &src, - T1 &&sz, unsigned align_dst, unsigned align_src, bool is_volatile=false) + T1 &&sz, Align align_dst, Align align_src, bool is_volatile=false) { auto src_ai = jl_aliasinfo_t::fromTBAA(ctx, src.tbaa); emit_memcpy_llvm(ctx, dst, dst_ai, data_pointer(ctx, src), src_ai, sz, align_dst, align_src, is_volatile); @@ -1972,7 +1971,7 @@ static jl_cgval_t typed_load(jl_codectx_t &ctx, Value *ptr, Value *idx_0based, j else if (!alignment) alignment = julia_alignment(jltype); if (intcast && Order == AtomicOrdering::NotAtomic) { - emit_memcpy(ctx, intcast, jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_stack), data, jl_aliasinfo_t::fromTBAA(ctx, tbaa), nb, alignment, intcast->getAlign().value()); + emit_memcpy(ctx, intcast, jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_stack), data, jl_aliasinfo_t::fromTBAA(ctx, tbaa), nb, Align(alignment), intcast->getAlign()); } else { if (!isboxed && jl_is_genericmemoryref_type(jltype)) { @@ -2093,12 +2092,15 @@ static jl_cgval_t typed_store(jl_codectx_t &ctx, FailOrder = AtomicOrdering::Monotonic; unsigned nb = isboxed ? sizeof(void*) : jl_datatype_size(jltype); AllocaInst *intcast = nullptr; + Type *intcast_eltyp = nullptr; + bool tracked_pointers = isboxed || CountTrackedPointers(elty).count > 0; if (!isboxed && Order != AtomicOrdering::NotAtomic && !elty->isIntOrPtrTy()) { + intcast_eltyp = elty; + elty = Type::getIntNTy(ctx.builder.getContext(), 8 * nb); if (!issetfield) { intcast = emit_static_alloca(ctx, elty); setName(ctx.emission_context, intcast, "atomic_store_box"); } - elty = Type::getIntNTy(ctx.builder.getContext(), 8 * nb); } Type *realelty = elty; if (Order != AtomicOrdering::NotAtomic && isa(elty)) { @@ -2107,14 +2109,20 @@ static jl_cgval_t typed_store(jl_codectx_t &ctx, elty = Type::getIntNTy(ctx.builder.getContext(), 8 * nb2); } Value *r = nullptr; - if (issetfield || isswapfield || isreplacefield || issetfieldonce) { - if (isboxed) + if (issetfield || isswapfield || isreplacefield || issetfieldonce) { // e.g. !ismodifyfield + assert(isboxed || rhs.typ == jltype); + if (isboxed) { r = boxed(ctx, rhs); - else if (aliasscope || Order != AtomicOrdering::NotAtomic || CountTrackedPointers(realelty).count) { + } + else if (intcast) { + emit_unbox_store(ctx, rhs, intcast, ctx.tbaa().tbaa_stack, intcast->getAlign()); + r = ctx.builder.CreateLoad(realelty, intcast); + } + else if (aliasscope || Order != AtomicOrdering::NotAtomic || tracked_pointers) { r = emit_unbox(ctx, realelty, rhs, jltype); - if (realelty != elty) - r = ctx.builder.CreateZExt(r, elty); } + if (realelty != elty) + r = ctx.builder.CreateZExt(r, elty); } Type *ptrty = PointerType::get(elty, ptr->getType()->getPointerAddressSpace()); if (ptr->getType() != ptrty) @@ -2152,7 +2160,7 @@ static jl_cgval_t typed_store(jl_codectx_t &ctx, } else { assert(Order == AtomicOrdering::NotAtomic && !isboxed && rhs.typ == jltype); - emit_unbox_store(ctx, rhs, ptr, tbaa, alignment); + emit_unbox_store(ctx, rhs, ptr, tbaa, Align(alignment)); } } else if (isswapfield) { @@ -2198,7 +2206,14 @@ static jl_cgval_t typed_store(jl_codectx_t &ctx, Current->addIncoming(instr, SkipBB); ctx.builder.SetInsertPoint(BB); } - Compare = emit_unbox(ctx, realelty, cmp, jltype); + cmp = update_julia_type(ctx, cmp, jltype); + if (intcast) { + emit_unbox_store(ctx, cmp, intcast, ctx.tbaa().tbaa_stack, intcast->getAlign()); + Compare = ctx.builder.CreateLoad(realelty, intcast); + } + else { + Compare = emit_unbox(ctx, realelty, cmp, jltype); + } if (realelty != elty) Compare = ctx.builder.CreateZExt(Compare, elty); } @@ -2245,16 +2260,17 @@ static jl_cgval_t typed_store(jl_codectx_t &ctx, if (realelty != elty) realCompare = ctx.builder.CreateTrunc(realCompare, realelty); if (intcast) { + assert(!isboxed); ctx.builder.CreateStore(realCompare, ctx.builder.CreateBitCast(intcast, realCompare->getType()->getPointerTo())); - if (maybe_null_if_boxed) - realCompare = ctx.builder.CreateLoad(intcast->getAllocatedType(), intcast); + if (tracked_pointers) + realCompare = ctx.builder.CreateLoad(intcast_eltyp, intcast); } - if (maybe_null_if_boxed) { - Value *first_ptr = isboxed ? Compare : extract_first_ptr(ctx, Compare); - if (first_ptr) - null_load_check(ctx, first_ptr, mod, var); + if (maybe_null_if_boxed && tracked_pointers) { + Value *first_ptr = isboxed ? realCompare : extract_first_ptr(ctx, realCompare); + assert(first_ptr); + null_load_check(ctx, first_ptr, mod, var); } - if (intcast) + if (intcast && !tracked_pointers) oldval = mark_julia_slot(intcast, jltype, NULL, ctx.tbaa().tbaa_stack); else oldval = mark_julia_type(ctx, realCompare, isboxed, jltype); @@ -2262,11 +2278,18 @@ static jl_cgval_t typed_store(jl_codectx_t &ctx, if (isboxed) { r = boxed(ctx, rhs); } - else if (Order != AtomicOrdering::NotAtomic || CountTrackedPointers(realelty).count) { + else if (intcast) { + emit_unbox_store(ctx, rhs, intcast, ctx.tbaa().tbaa_stack, intcast->getAlign()); + r = ctx.builder.CreateLoad(realelty, intcast); + if (!tracked_pointers) // oldval is a slot, so put the oldval back + ctx.builder.CreateStore(realCompare, intcast); + } + else if (Order != AtomicOrdering::NotAtomic) { + assert(!tracked_pointers); r = emit_unbox(ctx, realelty, rhs, jltype); - if (realelty != elty) - r = ctx.builder.CreateZExt(r, elty); } + if (realelty != elty) + r = ctx.builder.CreateZExt(r, elty); if (needlock) emit_lockstate_value(ctx, needlock, true); cmp = oldval; @@ -2304,7 +2327,7 @@ static jl_cgval_t typed_store(jl_codectx_t &ctx, } else { assert(!isboxed && rhs.typ == jltype); - emit_unbox_store(ctx, rhs, ptr, tbaa, alignment); + emit_unbox_store(ctx, rhs, ptr, tbaa, Align(alignment)); } ctx.builder.CreateBr(DoneBB); instr = load; @@ -2332,9 +2355,10 @@ static jl_cgval_t typed_store(jl_codectx_t &ctx, realinstr = ctx.builder.CreateTrunc(realinstr, realelty); if (intcast) { ctx.builder.CreateStore(realinstr, ctx.builder.CreateBitCast(intcast, realinstr->getType()->getPointerTo())); + // n.b. this oldval is only used for emit_f_is in this branch, so we know a priori that it does not need a gc-root oldval = mark_julia_slot(intcast, jltype, NULL, ctx.tbaa().tbaa_stack); if (maybe_null_if_boxed) - realinstr = ctx.builder.CreateLoad(intcast->getAllocatedType(), intcast); + realinstr = ctx.builder.CreateLoad(intcast_eltyp, intcast); } else { oldval = mark_julia_type(ctx, realinstr, isboxed, jltype); @@ -2374,20 +2398,23 @@ static jl_cgval_t typed_store(jl_codectx_t &ctx, ctx.builder.SetInsertPoint(DoneBB); if (needlock) emit_lockstate_value(ctx, needlock, false); - if (parent != NULL) { + if (parent != NULL && r && tracked_pointers && (!isboxed || !type_is_permalloc(rhs.typ))) { if (isreplacefield || issetfieldonce) { - // TODO: avoid this branch if we aren't making a write barrier BasicBlock *BB = BasicBlock::Create(ctx.builder.getContext(), "xchg_wb", ctx.f); DoneBB = BasicBlock::Create(ctx.builder.getContext(), "done_xchg_wb", ctx.f); ctx.builder.CreateCondBr(Success, BB, DoneBB); ctx.builder.SetInsertPoint(BB); } - if (r) { - if (!isboxed) - emit_write_multibarrier(ctx, parent, r, rhs.typ); - else if (!type_is_permalloc(rhs.typ)) - emit_write_barrier(ctx, parent, r); + if (realelty != elty) + r = ctx.builder.Insert(CastInst::Create(Instruction::Trunc, r, realelty)); + if (intcast) { + ctx.builder.CreateStore(r, intcast); + r = ctx.builder.CreateLoad(intcast_eltyp, intcast); } + if (!isboxed) + emit_write_multibarrier(ctx, parent, r, rhs.typ); + else if (!type_is_permalloc(rhs.typ)) + emit_write_barrier(ctx, parent, r); if (isreplacefield || issetfieldonce) { ctx.builder.CreateBr(DoneBB); ctx.builder.SetInsertPoint(DoneBB); @@ -2406,21 +2433,18 @@ static jl_cgval_t typed_store(jl_codectx_t &ctx, instr = ctx.builder.Insert(CastInst::Create(Instruction::Trunc, instr, realelty)); if (intcast) { ctx.builder.CreateStore(instr, ctx.builder.CreateBitCast(intcast, instr->getType()->getPointerTo())); - instr = nullptr; + if (tracked_pointers) + instr = ctx.builder.CreateLoad(intcast_eltyp, intcast); } - if (maybe_null_if_boxed) { - if (intcast) - instr = ctx.builder.CreateLoad(intcast->getAllocatedType(), intcast); + if (maybe_null_if_boxed && tracked_pointers) { Value *first_ptr = isboxed ? instr : extract_first_ptr(ctx, instr); - if (first_ptr) - null_load_check(ctx, first_ptr, mod, var); - if (intcast && !first_ptr) - instr = nullptr; + assert(first_ptr); + null_load_check(ctx, first_ptr, mod, var); } - if (instr) - oldval = mark_julia_type(ctx, instr, isboxed, jltype); - else + if (intcast && !tracked_pointers) oldval = mark_julia_slot(intcast, jltype, NULL, ctx.tbaa().tbaa_stack); + else + oldval = mark_julia_type(ctx, instr, isboxed, jltype); if (isreplacefield) { Success = ctx.builder.CreateZExt(Success, getInt8Ty(ctx.builder.getContext())); const jl_cgval_t argv[2] = {oldval, mark_julia_type(ctx, Success, false, jl_bool_type)}; @@ -2617,7 +2641,7 @@ static jl_cgval_t emit_unionload(jl_codectx_t &ctx, Value *addr, Value *ptindex, if (al > 1) lv->setAlignment(Align(al)); jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, tbaa); - emit_memcpy(ctx, lv, ai, addr, ai, fsz, al, al); + emit_memcpy(ctx, lv, ai, addr, ai, fsz, Align(al), Align(al)); addr = lv; } return mark_julia_slot(fsz > 0 ? addr : nullptr, jfty, tindex, tbaa); @@ -2960,12 +2984,12 @@ static Value *emit_genericmemoryowner(jl_codectx_t &ctx, Value *t) static Value *emit_allocobj(jl_codectx_t &ctx, jl_datatype_t *jt, bool fully_initialized); static void init_bits_value(jl_codectx_t &ctx, Value *newv, Value *v, MDNode *tbaa, - unsigned alignment = sizeof(void*)) // min alignment in julia's gc is pointer-aligned + Align alignment = Align(sizeof(void*))) // min alignment in julia's gc is pointer-aligned { jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, tbaa); // newv should already be tagged ai.decorateInst(ctx.builder.CreateAlignedStore(v, emit_bitcast(ctx, newv, - PointerType::get(v->getType(), 0)), Align(alignment))); + PointerType::get(v->getType(), 0)), alignment)); } static void init_bits_cgval(jl_codectx_t &ctx, Value *newv, const jl_cgval_t& v, MDNode *tbaa) @@ -2973,7 +2997,7 @@ static void init_bits_cgval(jl_codectx_t &ctx, Value *newv, const jl_cgval_t& v, // newv should already be tagged if (v.ispointer()) { unsigned align = std::max(julia_alignment(v.typ), (unsigned)sizeof(void*)); - emit_memcpy(ctx, newv, jl_aliasinfo_t::fromTBAA(ctx, tbaa), v, jl_datatype_size(v.typ), align, julia_alignment(v.typ)); + emit_memcpy(ctx, newv, jl_aliasinfo_t::fromTBAA(ctx, tbaa), v, jl_datatype_size(v.typ), Align(align), Align(julia_alignment(v.typ))); } else { init_bits_value(ctx, newv, v.V, tbaa); @@ -3432,7 +3456,7 @@ static void emit_unionmove(jl_codectx_t &ctx, Value *dest, MDNode *tbaa_dst, con if (jl_is_pointerfree(typ)) { unsigned alignment = julia_alignment(typ); if (!src.ispointer() || src.constant) { - emit_unbox_store(ctx, src, dest, tbaa_dst, alignment, isVolatile); + emit_unbox_store(ctx, src, dest, tbaa_dst, Align(alignment), isVolatile); } else { Value *src_ptr = data_pointer(ctx, src); @@ -3442,7 +3466,7 @@ static void emit_unionmove(jl_codectx_t &ctx, Value *dest, MDNode *tbaa_dst, con // if (skip) src_ptr = ctx.builder.CreateSelect(skip, dest, src_ptr); auto f = [&] { (void)emit_memcpy(ctx, dest, jl_aliasinfo_t::fromTBAA(ctx, tbaa_dst), src_ptr, - jl_aliasinfo_t::fromTBAA(ctx, src.tbaa), nb, alignment, alignment, isVolatile); + jl_aliasinfo_t::fromTBAA(ctx, src.tbaa), nb, Align(alignment), Align(alignment), isVolatile); return nullptr; }; if (skip) @@ -3479,7 +3503,7 @@ static void emit_unionmove(jl_codectx_t &ctx, Value *dest, MDNode *tbaa_dst, con return; } else { emit_memcpy(ctx, dest, jl_aliasinfo_t::fromTBAA(ctx, tbaa_dst), src_ptr, - jl_aliasinfo_t::fromTBAA(ctx, src.tbaa), nb, alignment, alignment, isVolatile); + jl_aliasinfo_t::fromTBAA(ctx, src.tbaa), nb, Align(alignment), Align(alignment), isVolatile); } } ctx.builder.CreateBr(postBB); @@ -3505,7 +3529,7 @@ static void emit_unionmove(jl_codectx_t &ctx, Value *dest, MDNode *tbaa_dst, con Value *datatype = emit_typeof(ctx, src, false, false); Value *copy_bytes = emit_datatype_size(ctx, datatype); (void)emit_memcpy(ctx, dest, jl_aliasinfo_t::fromTBAA(ctx, tbaa_dst), data_pointer(ctx, src), - jl_aliasinfo_t::fromTBAA(ctx, src.tbaa), copy_bytes, 1, 1, isVolatile); + jl_aliasinfo_t::fromTBAA(ctx, src.tbaa), copy_bytes, Align(1), Align(1), isVolatile); return nullptr; }; if (skip) @@ -3800,8 +3824,6 @@ static jl_cgval_t emit_new_struct(jl_codectx_t &ctx, jl_value_t *ty, size_t narg else { strct = emit_static_alloca(ctx, lt); setName(ctx.emission_context, strct, arg_typename); - if (nargs < nf) - promotion_point = ctx.builder.CreateStore(ctx.builder.CreateFreeze(UndefValue::get(lt)), strct); if (tracked.count) undef_derived_strct(ctx, strct, sty, ctx.tbaa().tbaa_stack); } @@ -3924,7 +3946,7 @@ static jl_cgval_t emit_new_struct(jl_codectx_t &ctx, jl_value_t *ty, size_t narg } else if (init_as_value) { fval = emit_unbox(ctx, fty, fval_info, jtype); } else { - emit_unbox_store(ctx, fval_info, dest, ctx.tbaa().tbaa_stack, jl_field_align(sty, i)); + emit_unbox_store(ctx, fval_info, dest, ctx.tbaa().tbaa_stack, Align(jl_field_align(sty, i))); } } if (init_as_value) { @@ -3958,6 +3980,14 @@ static jl_cgval_t emit_new_struct(jl_codectx_t &ctx, jl_value_t *ty, size_t narg } } } + if (promotion_point && nargs < nf) { + assert(!init_as_value); + IRBuilderBase::InsertPoint savedIP = ctx.builder.saveIP(); + ctx.builder.SetInsertPoint(promotion_point); + promotion_point = cast(ctx.builder.CreateFreeze(UndefValue::get(lt))); + ctx.builder.CreateStore(promotion_point, strct); + ctx.builder.restoreIP(savedIP); + } if (type_is_ghost(lt)) return mark_julia_const(ctx, sty->instance); else if (init_as_value) @@ -4144,7 +4174,7 @@ static jl_cgval_t emit_memoryref(jl_codectx_t &ctx, const jl_cgval_t &ref, jl_cg boffset = ctx.builder.CreateMul(offset, elsz); #if 0 // TODO: if opaque-pointers? newdata = emit_bitcast(ctx, data, getInt8PtrTy(ctx.builder.getContext())); - newdata = ctx.builder.CreateGEP(getInt8Ty(ctx.builder.getContext()), newdata, boffset); + newdata = ctx.builder.CreateInBoundsGEP(getInt8Ty(ctx.builder.getContext()), newdata, boffset); #else Type *elty = isboxed ? ctx.types().T_prjlvalue : julia_type_to_llvm(ctx, jl_tparam1(ref.typ)); newdata = emit_bitcast(ctx, data, elty->getPointerTo(0)); diff --git a/src/codegen-stubs.c b/src/codegen-stubs.c index 30026b0876a3c..7bde6e8948a13 100644 --- a/src/codegen-stubs.c +++ b/src/codegen-stubs.c @@ -107,8 +107,6 @@ JL_DLLEXPORT uint64_t jl_getUnwindInfo_fallback(uint64_t dwAddr) return 0; } -JL_DLLEXPORT void jl_build_newpm_pipeline_fallback(void *MPM, void *PB, void *config) UNAVAILABLE - JL_DLLEXPORT void jl_register_passbuilder_callbacks_fallback(void *PB) { } #define MODULE_PASS(NAME, CLASS, CREATE_PASS) \ diff --git a/src/codegen.cpp b/src/codegen.cpp index ad13cf8d1124e..aca3f8e2c5cd1 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -5512,7 +5512,7 @@ static jl_cgval_t emit_varinfo(jl_codectx_t &ctx, jl_varinfo_t &vi, jl_sym_t *va else { const DataLayout &DL = jl_Module->getDataLayout(); uint64_t sz = DL.getTypeStoreSize(T); - emit_memcpy(ctx, ssaslot, jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_stack), vi.value, sz, ssaslot->getAlign().value(), varslot->getAlign().value()); + emit_memcpy(ctx, ssaslot, jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_stack), vi.value, sz, ssaslot->getAlign(), varslot->getAlign()); } Value *tindex = NULL; if (vi.pTIndex) @@ -5620,8 +5620,9 @@ static void emit_vi_assignment_unboxed(jl_codectx_t &ctx, jl_varinfo_t &vi, Valu // This check should probably mostly catch the relevant situations. if (vi.value.V != rval_info.V) { Value *copy_bytes = ConstantInt::get(getInt32Ty(ctx.builder.getContext()), jl_datatype_size(vi.value.typ)); + Align alignment(julia_alignment(rval_info.typ)); emit_memcpy(ctx, vi.value.V, jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_stack), rval_info, copy_bytes, - julia_alignment(rval_info.typ), julia_alignment(rval_info.typ), vi.isVolatile); + alignment, alignment, vi.isVolatile); } } else { @@ -6698,8 +6699,9 @@ static void emit_cfunc_invalidate( root1 = ctx.builder.CreateConstInBoundsGEP2_32(get_returnroots_type(ctx, return_roots), root1, 0, 0); ctx.builder.CreateStore(gf_ret, root1); } + Align alignment(julia_alignment(rettype)); emit_memcpy(ctx, &*gf_thunk->arg_begin(), jl_aliasinfo_t::fromTBAA(ctx, nullptr), gf_ret, - jl_aliasinfo_t::fromTBAA(ctx, nullptr), jl_datatype_size(rettype), julia_alignment(rettype), julia_alignment(rettype)); + jl_aliasinfo_t::fromTBAA(ctx, nullptr), jl_datatype_size(rettype), Align(alignment), Align(alignment)); ctx.builder.CreateRetVoid(); break; } @@ -8566,7 +8568,7 @@ static jl_llvm_functions_t jl_cgval_t closure_world = typed_load(ctx, worldaddr, NULL, (jl_value_t*)jl_long_type, nullptr, nullptr, false, AtomicOrdering::NotAtomic, false, ctx.types().alignof_ptr.value()); - emit_unbox_store(ctx, closure_world, world_age_field, ctx.tbaa().tbaa_gcframe, ctx.types().alignof_ptr.value()); + emit_unbox_store(ctx, closure_world, world_age_field, ctx.tbaa().tbaa_gcframe, ctx.types().alignof_ptr); // Load closure env Value *envaddr = ctx.builder.CreateInBoundsGEP( @@ -9073,8 +9075,9 @@ static jl_llvm_functions_t } if (returninfo.cc == jl_returninfo_t::SRet) { assert(jl_is_concrete_type(jlrettype)); + Align alignment(julia_alignment(jlrettype)); emit_memcpy(ctx, sret, jl_aliasinfo_t::fromTBAA(ctx, nullptr), retvalinfo, - jl_datatype_size(jlrettype), julia_alignment(jlrettype), julia_alignment(jlrettype)); + jl_datatype_size(jlrettype), alignment, alignment); } else { // must be jl_returninfo_t::Union emit_unionmove(ctx, sret, nullptr, retvalinfo, /*skip*/isboxed_union); @@ -9307,7 +9310,7 @@ static jl_llvm_functions_t // load of val) if the runtime type of val isn't phiType Value *isvalid = emit_isa_and_defined(ctx, val, phiType); emit_guarded_test(ctx, isvalid, nullptr, [&] { - emit_unbox_store(ctx, update_julia_type(ctx, val, phiType), dest, ctx.tbaa().tbaa_stack, julia_alignment(phiType)); + emit_unbox_store(ctx, update_julia_type(ctx, val, phiType), dest, ctx.tbaa().tbaa_stack, Align(julia_alignment(phiType))); return nullptr; }); } @@ -9334,7 +9337,7 @@ static jl_llvm_functions_t if (VN) V = Constant::getNullValue(ctx.types().T_prjlvalue); if (dest) - emit_unbox_store(ctx, val, dest, ctx.tbaa().tbaa_stack, julia_alignment(val.typ)); + emit_unbox_store(ctx, val, dest, ctx.tbaa().tbaa_stack, Align(julia_alignment(val.typ))); RTindex = ConstantInt::get(getInt8Ty(ctx.builder.getContext()), tindex); } } diff --git a/src/datatype.c b/src/datatype.c index 51115c032118c..8de401f4dd0f7 100644 --- a/src/datatype.c +++ b/src/datatype.c @@ -2176,7 +2176,7 @@ inline int setonce_bits(jl_datatype_t *rty, char *p, jl_value_t *parent, jl_valu } else { char *px = lock(p, parent, needlock, isatomic); - success = undefref_check(rty, (jl_value_t*)px) != NULL; + success = undefref_check(rty, (jl_value_t*)px) == NULL; if (success) memassign_safe(hasptr, px, rhs, fsz); unlock(p, parent, needlock, isatomic); diff --git a/src/debug-registry.h b/src/debug-registry.h index f30049eb5b210..85a94245ce6aa 100644 --- a/src/debug-registry.h +++ b/src/debug-registry.h @@ -99,18 +99,26 @@ class JITDebugInfoRegistry }; private: - struct ObjectInfo { - const llvm::object::ObjectFile *object = nullptr; - size_t SectionSize = 0; - ptrdiff_t slide = 0; - llvm::object::SectionRef Section{}; - llvm::DIContext *context = nullptr; + struct LazyObjectInfo { + SmallVector data; + size_t uncompressedsize; + std::unique_ptr object; + std::unique_ptr context; + LazyObjectInfo() = delete; + }; + + struct SectionInfo { + LazyObjectInfo *object; + size_t SectionSize; + ptrdiff_t slide; + uint64_t SectionIndex; + SectionInfo() = delete; }; template using rev_map = std::map>; - typedef rev_map objectmap_t; + typedef rev_map objectmap_t; typedef rev_map objfilemap_t; objectmap_t objectmap{}; diff --git a/src/debuginfo.cpp b/src/debuginfo.cpp index 5cb7401a036d4..84550811072fe 100644 --- a/src/debuginfo.cpp +++ b/src/debuginfo.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -335,8 +336,12 @@ void JITDebugInfoRegistry::registerJITObject(const object::ObjectFile &Object, #endif // defined(_OS_X86_64_) #endif // defined(_OS_WINDOWS_) + SmallVector packed; + compression::zlib::compress(ArrayRef((uint8_t*)Object.getData().data(), Object.getData().size()), packed, compression::zlib::DefaultCompression); + jl_jit_add_bytes(packed.size()); + auto ObjectCopy = new LazyObjectInfo{packed, Object.getData().size()}; // intentionally leaked so that we don't need to ref-count it, intentionally copied so that we exact-size the allocation (since no shrink_to_fit function) auto symbols = object::computeSymbolSizes(Object); - bool first = true; + bool hassection = false; for (const auto &sym_size : symbols) { const object::SymbolRef &sym_iter = sym_size.first; object::SymbolRef::Type SymbolType = cantFail(sym_iter.getType()); @@ -385,17 +390,17 @@ void JITDebugInfoRegistry::registerJITObject(const object::ObjectFile &Object, jl_profile_atomic([&]() JL_NOTSAFEPOINT { if (mi) linfomap[Addr] = std::make_pair(Size, mi); - if (first) { - objectmap[SectionLoadAddr] = {&Object, - (size_t)SectionSize, - (ptrdiff_t)(SectionAddr - SectionLoadAddr), - *Section, - nullptr, - }; - first = false; - } + hassection = true; + objectmap.insert(std::pair{SectionLoadAddr, SectionInfo{ + ObjectCopy, + (size_t)SectionSize, + (ptrdiff_t)(SectionAddr - SectionLoadAddr), + Section->getIndex() + }}); }); } + if (!hassection) // clang-sa demands that we do this to fool cplusplus.NewDeleteLeaks + delete ObjectCopy; } void jl_register_jit_object(const object::ObjectFile &Object, @@ -1213,11 +1218,33 @@ int jl_DI_for_fptr(uint64_t fptr, uint64_t *symsize, int64_t *slide, auto fit = objmap.lower_bound(fptr); if (fit != objmap.end() && fptr < fit->first + fit->second.SectionSize) { *slide = fit->second.slide; - *Section = fit->second.Section; - if (context) { - if (fit->second.context == nullptr) - fit->second.context = DWARFContext::create(*fit->second.object).release(); - *context = fit->second.context; + auto lazyobject = fit->second.object; + if (!lazyobject->object && !lazyobject->data.empty()) { + if (lazyobject->uncompressedsize) { + SmallVector unpacked; + Error E = compression::zlib::decompress(lazyobject->data, unpacked, lazyobject->uncompressedsize); + if (E) + lazyobject->data.clear(); + else + lazyobject->data = std::move(unpacked); + jl_jit_add_bytes(lazyobject->data.size() - lazyobject->uncompressedsize); + lazyobject->uncompressedsize = 0; + } + if (!lazyobject->data.empty()) { + auto obj = object::ObjectFile::createObjectFile(MemoryBufferRef(StringRef((const char*)lazyobject->data.data(), lazyobject->data.size()), "jit.o")); + if (obj) + lazyobject->object = std::move(*obj); + else + lazyobject->data.clear(); + } + } + if (lazyobject->object) { + *Section = *std::next(lazyobject->object->section_begin(), fit->second.SectionIndex); + if (context) { + if (lazyobject->context == nullptr) + lazyobject->context = DWARFContext::create(*lazyobject->object); + *context = lazyobject->context.get(); + } } found = 1; } diff --git a/src/debuginfo.h b/src/debuginfo.h index 5b5cdcb82d534..6cd7528910765 100644 --- a/src/debuginfo.h +++ b/src/debuginfo.h @@ -1,6 +1,7 @@ // This file is a part of Julia. License is MIT: https://julialang.org/license // Declarations for debuginfo.cpp +void jl_jit_add_bytes(size_t bytes) JL_NOTSAFEPOINT; int jl_DI_for_fptr(uint64_t fptr, uint64_t *symsize, int64_t *slide, llvm::object::SectionRef *Section, llvm::DIContext **context) JL_NOTSAFEPOINT; diff --git a/src/gc.c b/src/gc.c index bb63a1725af2e..fd9ad71d8afe0 100644 --- a/src/gc.c +++ b/src/gc.c @@ -1430,7 +1430,7 @@ STATIC_INLINE void gc_dump_page_utilization_data(void) JL_NOTSAFEPOINT } } -int64_t buffered_pages = 0; +_Atomic(int64_t) buffered_pages = 0; // Returns pointer to terminal pointer of list rooted at *pfl. static void gc_sweep_page(gc_page_profiler_serializer_t *s, jl_gc_pool_t *p, jl_gc_page_stack_t *allocd, jl_gc_page_stack_t *buffered, @@ -1461,8 +1461,8 @@ static void gc_sweep_page(gc_page_profiler_serializer_t *s, jl_gc_pool_t *p, jl_ // the eager one uses less memory. // FIXME - need to do accounting on a per-thread basis // on quick sweeps, keep a few pages empty but allocated for performance - if (!current_sweep_full && buffered_pages <= default_collect_interval / GC_PAGE_SZ) { - buffered_pages++; + if (!current_sweep_full && jl_atomic_load_relaxed(&buffered_pages) <= default_collect_interval / GC_PAGE_SZ) { + jl_atomic_fetch_add_relaxed(&buffered_pages, 1); keep_as_local_buffer = 1; } #endif @@ -1756,7 +1756,7 @@ void gc_free_pages(void) static void gc_sweep_pool(void) { gc_time_pool_start(); - buffered_pages = 0; + jl_atomic_store_relaxed(&buffered_pages, 0); // For the benefit of the analyzer, which doesn't know that gc_n_threads // doesn't change over the course of this function @@ -1800,7 +1800,7 @@ static void gc_sweep_pool(void) jl_gc_pagemeta_t *pg = jl_atomic_load_relaxed(&ptls2->page_metadata_buffered.bottom); while (pg != NULL) { jl_gc_pagemeta_t *pg2 = pg->next; - buffered_pages++; + jl_atomic_fetch_add_relaxed(&buffered_pages, 1); pg = pg2; } } diff --git a/src/gc.h b/src/gc.h index 4df6aceb80734..01d8745b2899e 100644 --- a/src/gc.h +++ b/src/gc.h @@ -441,7 +441,6 @@ extern jl_gc_num_t gc_num; extern bigval_t *big_objects_marked; extern arraylist_t finalizer_list_marked; extern arraylist_t to_finalize; -extern int64_t buffered_pages; extern int gc_first_tid; extern int gc_n_threads; extern jl_ptls_t* gc_all_tls_states; diff --git a/src/gf.c b/src/gf.c index 9f92647244c67..c9e7391b2fd8a 100644 --- a/src/gf.c +++ b/src/gf.c @@ -3436,7 +3436,7 @@ static int sort_mlmatches(jl_array_t *t, size_t idx, arraylist_t *visited, array int msp2 = !msp && jl_type_morespecific((jl_value_t*)m2->sig, (jl_value_t*)m->sig); if (!msp) { if (subt || !include_ambiguous || (lim != -1 && msp2)) { - if (subt2 || jl_subtype((jl_value_t*)ti, m2->sig)) { + if (subt2 || ((lim != -1 || (!include_ambiguous && !msp2)) && jl_subtype((jl_value_t*)ti, m2->sig))) { // this may be filtered out as fully intersected, if applicable later mayexclude = 1; } diff --git a/src/intrinsics.cpp b/src/intrinsics.cpp index aacf1e495beff..512738ef8a161 100644 --- a/src/intrinsics.cpp +++ b/src/intrinsics.cpp @@ -403,7 +403,7 @@ static Value *emit_unboxed_coercion(jl_codectx_t &ctx, Type *to, Value *unboxed) } else if (!ty->isIntOrPtrTy() && !ty->isFloatingPointTy()) { assert(DL.getTypeSizeInBits(ty) == DL.getTypeSizeInBits(to)); - AllocaInst *cast = ctx.builder.CreateAlloca(ty); + AllocaInst *cast = emit_static_alloca(ctx, ty); setName(ctx.emission_context, cast, "coercion"); ctx.builder.CreateStore(unboxed, cast); unboxed = ctx.builder.CreateLoad(to, ctx.builder.CreateBitCast(cast, to->getPointerTo())); @@ -497,7 +497,7 @@ static Value *emit_unbox(jl_codectx_t &ctx, Type *to, const jl_cgval_t &x, jl_va } // emit code to store a raw value into a destination -static void emit_unbox_store(jl_codectx_t &ctx, const jl_cgval_t &x, Value *dest, MDNode *tbaa_dest, unsigned alignment, bool isVolatile) +static void emit_unbox_store(jl_codectx_t &ctx, const jl_cgval_t &x, Value *dest, MDNode *tbaa_dest, Align alignment, bool isVolatile) { if (x.isghost) { // this can happen when a branch yielding a different type ends @@ -512,7 +512,7 @@ static void emit_unbox_store(jl_codectx_t &ctx, const jl_cgval_t &x, Value *dest Type *dest_ty = unboxed->getType()->getPointerTo(); if (dest->getType() != dest_ty) dest = emit_bitcast(ctx, dest, dest_ty); - StoreInst *store = ctx.builder.CreateAlignedStore(unboxed, dest, Align(alignment)); + StoreInst *store = ctx.builder.CreateAlignedStore(unboxed, dest, alignment); store->setVolatile(isVolatile); jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, tbaa_dest); ai.decorateInst(store); @@ -520,7 +520,7 @@ static void emit_unbox_store(jl_codectx_t &ctx, const jl_cgval_t &x, Value *dest } Value *src = data_pointer(ctx, x); - emit_memcpy(ctx, dest, jl_aliasinfo_t::fromTBAA(ctx, tbaa_dest), src, jl_aliasinfo_t::fromTBAA(ctx, x.tbaa), jl_datatype_size(x.typ), alignment, julia_alignment(x.typ), isVolatile); + emit_memcpy(ctx, dest, jl_aliasinfo_t::fromTBAA(ctx, tbaa_dest), src, jl_aliasinfo_t::fromTBAA(ctx, x.tbaa), jl_datatype_size(x.typ), Align(alignment), Align(julia_alignment(x.typ)), isVolatile); } static jl_datatype_t *staticeval_bitstype(const jl_cgval_t &targ) @@ -771,7 +771,7 @@ static jl_cgval_t emit_pointerref(jl_codectx_t &ctx, ArrayRef argv) thePtr = ctx.builder.CreateInBoundsGEP(getInt8Ty(ctx.builder.getContext()), emit_bitcast(ctx, thePtr, getInt8PtrTy(ctx.builder.getContext())), im1); setName(ctx.emission_context, thePtr, "pointerref_src"); MDNode *tbaa = best_tbaa(ctx.tbaa(), ety); - emit_memcpy(ctx, strct, jl_aliasinfo_t::fromTBAA(ctx, tbaa), thePtr, jl_aliasinfo_t::fromTBAA(ctx, nullptr), size, sizeof(jl_value_t*), align_nb); + emit_memcpy(ctx, strct, jl_aliasinfo_t::fromTBAA(ctx, tbaa), thePtr, jl_aliasinfo_t::fromTBAA(ctx, nullptr), size, Align(sizeof(jl_value_t*)), Align(align_nb)); return mark_julia_type(ctx, strct, true, ety); } else { @@ -800,7 +800,7 @@ static jl_cgval_t emit_runtime_pointerset(jl_codectx_t &ctx, ArrayRef argv) { const jl_cgval_t &e = argv[0]; - const jl_cgval_t &x = argv[1]; + jl_cgval_t x = argv[1]; const jl_cgval_t &i = argv[2]; const jl_cgval_t &align = argv[3]; @@ -823,6 +823,9 @@ static jl_cgval_t emit_pointerset(jl_codectx_t &ctx, ArrayRef argv) return jl_cgval_t(); } emit_typecheck(ctx, x, ety, "pointerset"); + x = update_julia_type(ctx, x, ety); + if (x.typ == jl_bottom_type) + return jl_cgval_t(); Value *idx = emit_unbox(ctx, ctx.types().T_size, i, (jl_value_t*)jl_long_type); Value *im1 = ctx.builder.CreateSub(idx, ConstantInt::get(ctx.types().T_size, 1)); @@ -848,7 +851,7 @@ static jl_cgval_t emit_pointerset(jl_codectx_t &ctx, ArrayRef argv) setName(ctx.emission_context, im1, "pointerset_offset"); auto gep = ctx.builder.CreateInBoundsGEP(getInt8Ty(ctx.builder.getContext()), thePtr, im1); setName(ctx.emission_context, gep, "pointerset_ptr"); - emit_memcpy(ctx, gep, jl_aliasinfo_t::fromTBAA(ctx, nullptr), x, size, align_nb, julia_alignment(ety)); + emit_memcpy(ctx, gep, jl_aliasinfo_t::fromTBAA(ctx, nullptr), x, size, Align(align_nb), Align(julia_alignment(ety))); } else { bool isboxed; @@ -966,7 +969,7 @@ static jl_cgval_t emit_atomic_pointerop(jl_codectx_t &ctx, intrinsic f, ArrayRef bool ismodifyfield = f == atomic_pointermodify; const jl_cgval_t undefval; const jl_cgval_t &e = argv[0]; - const jl_cgval_t &x = isreplacefield || ismodifyfield ? argv[2] : argv[1]; + jl_cgval_t x = isreplacefield || ismodifyfield ? argv[2] : argv[1]; const jl_cgval_t &y = isreplacefield || ismodifyfield ? argv[1] : undefval; const jl_cgval_t &ord = isreplacefield || ismodifyfield ? argv[3] : argv[2]; const jl_cgval_t &failord = isreplacefield ? argv[4] : undefval; @@ -1008,8 +1011,12 @@ static jl_cgval_t emit_atomic_pointerop(jl_codectx_t &ctx, intrinsic f, ArrayRef emit_error(ctx, msg); return jl_cgval_t(); } - if (!ismodifyfield) + if (!ismodifyfield) { emit_typecheck(ctx, x, ety, std::string(jl_intrinsic_name((int)f))); + x = update_julia_type(ctx, x, ety); + if (x.typ == jl_bottom_type) + return jl_cgval_t(); + } size_t nb = jl_datatype_size(ety); if ((nb & (nb - 1)) != 0 || nb > MAX_POINTERATOMIC_SIZE) { diff --git a/src/jitlayers.cpp b/src/jitlayers.cpp index 2fb99c7ddba2b..d7fef443771bc 100644 --- a/src/jitlayers.cpp +++ b/src/jitlayers.cpp @@ -800,22 +800,19 @@ struct JITObjectInfo { class JLDebuginfoPlugin : public ObjectLinkingLayer::Plugin { std::mutex PluginMutex; std::map> PendingObjs; - // Resources from distinct `MaterializationResponsibility`s can get merged - // after emission, so we can have multiple debug objects per resource key. - std::map, 0>> RegisteredObjs; public: void notifyMaterializing(MaterializationResponsibility &MR, jitlink::LinkGraph &G, jitlink::JITLinkContext &Ctx, MemoryBufferRef InputObject) override { - // Keeping around a full copy of the input object file (and re-parsing it) is - // wasteful, but for now, this lets us reuse the existing debuginfo.cpp code. - // Should look into just directly pulling out all the information required in - // a JITLink pass and just keeping the required tables/DWARF sections around - // (perhaps using the LLVM DebuggerSupportPlugin as a reference). auto NewBuffer = MemoryBuffer::getMemBufferCopy(InputObject.getBuffer(), G.getName()); + // Re-parsing the InputObject is wasteful, but for now, this lets us + // reuse the existing debuginfo.cpp code. Should look into just + // directly pulling out all the information required in a JITLink pass + // and just keeping the required tables/DWARF sections around (perhaps + // using the LLVM DebuggerSupportPlugin as a reference). auto NewObj = cantFail(object::ObjectFile::createObjectFile(NewBuffer->getMemBufferRef())); @@ -849,13 +846,8 @@ class JLDebuginfoPlugin : public ObjectLinkingLayer::Plugin { }; jl_register_jit_object(*NewInfo->Object, getLoadAddress, nullptr); - } - - cantFail(MR.withResourceKeyDo([&](ResourceKey K) { - std::lock_guard lock(PluginMutex); - RegisteredObjs[K].push_back(std::move(PendingObjs[&MR])); PendingObjs.erase(&MR); - })); + } return Error::success(); } @@ -866,32 +858,23 @@ class JLDebuginfoPlugin : public ObjectLinkingLayer::Plugin { PendingObjs.erase(&MR); return Error::success(); } + #if JL_LLVM_VERSION >= 160000 Error notifyRemovingResources(JITDylib &JD, orc::ResourceKey K) override #else - Error notifyRemovingResources(ResourceKey K) override + Error notifyRemovingResources(orc::ResourceKey K) override #endif { - std::lock_guard lock(PluginMutex); - RegisteredObjs.erase(K); - // TODO: If we ever unload code, need to notify debuginfo registry. return Error::success(); } #if JL_LLVM_VERSION >= 160000 - void notifyTransferringResources(JITDylib &JD, ResourceKey DstKey, ResourceKey SrcKey) override + void notifyTransferringResources(JITDylib &JD, orc::ResourceKey DstKey, + orc::ResourceKey SrcKey) override {} #else - void notifyTransferringResources(ResourceKey DstKey, ResourceKey SrcKey) override + void notifyTransferringResources(orc::ResourceKey DstKey, + orc::ResourceKey SrcKey) override {} #endif - { - std::lock_guard lock(PluginMutex); - auto SrcIt = RegisteredObjs.find(SrcKey); - if (SrcIt != RegisteredObjs.end()) { - for (std::unique_ptr &Info : SrcIt->second) - RegisteredObjs[DstKey].push_back(std::move(Info)); - RegisteredObjs.erase(SrcIt); - } - } void modifyPassConfig(MaterializationResponsibility &MR, jitlink::LinkGraph &, jitlink::PassConfiguration &PassConfig) override @@ -931,12 +914,12 @@ class JLDebuginfoPlugin : public ObjectLinkingLayer::Plugin { class JLMemoryUsagePlugin : public ObjectLinkingLayer::Plugin { private: - std::atomic &total_size; + std::atomic &jit_bytes_size; public: - JLMemoryUsagePlugin(std::atomic &total_size) - : total_size(total_size) {} + JLMemoryUsagePlugin(std::atomic &jit_bytes_size) + : jit_bytes_size(jit_bytes_size) {} Error notifyFailed(orc::MaterializationResponsibility &MR) override { return Error::success(); @@ -985,7 +968,7 @@ class JLMemoryUsagePlugin : public ObjectLinkingLayer::Plugin { } (void) code_size; (void) data_size; - this->total_size.fetch_add(graph_size, std::memory_order_relaxed); + this->jit_bytes_size.fetch_add(graph_size, std::memory_order_relaxed); jl_timing_counter_inc(JL_TIMING_COUNTER_JITSize, graph_size); jl_timing_counter_inc(JL_TIMING_COUNTER_JITCodeSize, code_size); jl_timing_counter_inc(JL_TIMING_COUNTER_JITDataSize, data_size); @@ -1101,24 +1084,7 @@ void registerRTDyldJITObject(const object::ObjectFile &Object, const RuntimeDyld::LoadedObjectInfo &L, const std::shared_ptr &MemMgr) { - auto SavedObject = L.getObjectForDebug(Object).takeBinary(); - // If the debug object is unavailable, save (a copy of) the original object - // for our backtraces. - // This copy seems unfortunate, but there doesn't seem to be a way to take - // ownership of the original buffer. - if (!SavedObject.first) { - auto NewBuffer = - MemoryBuffer::getMemBufferCopy(Object.getData(), Object.getFileName()); - auto NewObj = - cantFail(object::ObjectFile::createObjectFile(NewBuffer->getMemBufferRef())); - SavedObject = std::make_pair(std::move(NewObj), std::move(NewBuffer)); - } - const object::ObjectFile *DebugObj = SavedObject.first.release(); - SavedObject.second.release(); - StringMap loadedSections; - // Use the original Object, not the DebugObject, as this is used for the - // RuntimeDyld::LoadedObjectInfo lookup. for (const object::SectionRef &lSection : Object.sections()) { auto sName = lSection.getName(); if (sName) { @@ -1135,7 +1101,9 @@ void registerRTDyldJITObject(const object::ObjectFile &Object, return L.getSectionLoadAddress(search->second); }; - jl_register_jit_object(*DebugObj, getLoadAddress, + auto DebugObject = L.getObjectForDebug(Object); // ELF requires us to make a copy to mutate the header with the section load addresses. On other platforms this is a no-op. + jl_register_jit_object(DebugObject.getBinary() ? *DebugObject.getBinary() : Object, + getLoadAddress, #if defined(_OS_WINDOWS_) && defined(_CPU_X86_64_) [MemMgr](void *p) { return lookupWriteAddressFor(MemMgr.get(), p); } #else @@ -1737,7 +1705,7 @@ JuliaOJIT::JuliaOJIT() ES, std::move(ehRegistrar))); ObjectLayer.addPlugin(std::make_unique()); - ObjectLayer.addPlugin(std::make_unique(total_size)); + ObjectLayer.addPlugin(std::make_unique(jit_bytes_size)); #else ObjectLayer.setNotifyLoaded( [this](orc::MaterializationResponsibility &MR, @@ -2058,19 +2026,20 @@ std::string JuliaOJIT::getMangledName(const GlobalValue *GV) return getMangledName(GV->getName()); } -#ifdef JL_USE_JITLINK size_t JuliaOJIT::getTotalBytes() const { - return total_size.load(std::memory_order_relaxed); + auto bytes = jit_bytes_size.load(std::memory_order_relaxed); +#ifndef JL_USE_JITLINK + size_t getRTDyldMemoryManagerTotalBytes(RTDyldMemoryManager *mm) JL_NOTSAFEPOINT; + bytes += getRTDyldMemoryManagerTotalBytes(MemMgr.get()); +#endif + return bytes; } -#else -size_t getRTDyldMemoryManagerTotalBytes(RTDyldMemoryManager *mm) JL_NOTSAFEPOINT; -size_t JuliaOJIT::getTotalBytes() const +void JuliaOJIT::addBytes(size_t bytes) { - return getRTDyldMemoryManagerTotalBytes(MemMgr.get()); + jit_bytes_size.fetch_add(bytes, std::memory_order_relaxed); } -#endif void JuliaOJIT::printTimers() { @@ -2348,3 +2317,9 @@ size_t jl_jit_total_bytes_impl(void) { return jl_ExecutionEngine->getTotalBytes(); } + +// API for adding bytes to record being owned by the JIT +void jl_jit_add_bytes(size_t bytes) +{ + jl_ExecutionEngine->addBytes(bytes); +} diff --git a/src/jitlayers.h b/src/jitlayers.h index 7d35ff7f18034..41383a3b970c0 100644 --- a/src/jitlayers.h +++ b/src/jitlayers.h @@ -553,6 +553,7 @@ class JuliaOJIT { TargetIRAnalysis getTargetIRAnalysis() const JL_NOTSAFEPOINT; size_t getTotalBytes() const JL_NOTSAFEPOINT; + void addBytes(size_t bytes) JL_NOTSAFEPOINT; void printTimers() JL_NOTSAFEPOINT; jl_locked_stream &get_dump_emitted_mi_name_stream() JL_NOTSAFEPOINT { @@ -597,10 +598,10 @@ class JuliaOJIT { ResourcePool> ContextPool; + std::atomic jit_bytes_size{0}; #ifndef JL_USE_JITLINK const std::shared_ptr MemMgr; #else - std::atomic total_size{0}; const std::unique_ptr MemMgr; #endif ObjLayerT ObjectLayer; diff --git a/src/jl_exported_funcs.inc b/src/jl_exported_funcs.inc index 5ecff60813a4f..05057cfd80861 100644 --- a/src/jl_exported_funcs.inc +++ b/src/jl_exported_funcs.inc @@ -553,7 +553,6 @@ YY(jl_type_to_llvm) \ YY(jl_getUnwindInfo) \ YY(jl_get_libllvm) \ - YY(jl_build_newpm_pipeline) \ YY(jl_register_passbuilder_callbacks) \ YY(LLVMExtraMPMAddCPUFeaturesPass) \ YY(LLVMExtraMPMAddRemoveNIPass) \ diff --git a/src/jltypes.c b/src/jltypes.c index cb03b493d8598..ebf3fe3b5f640 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -1913,7 +1913,7 @@ static jl_value_t *jl_tupletype_fill(size_t n, jl_value_t *t, int check, int not t = normalize_unionalls(t); p = t; jl_value_t *tw = extract_wrapper(t); - if (tw && t != tw && jl_types_equal(t, tw)) + if (tw && t != tw && !jl_has_free_typevars(t) && jl_types_equal(t, tw)) t = tw; p = t; check = 0; // remember that checks are already done now @@ -1997,7 +1997,7 @@ static jl_value_t *inst_datatype_inner(jl_datatype_t *dt, jl_svec_t *p, jl_value // normalize types equal to wrappers (prepare for Typeofwrapper) jl_value_t *tw = extract_wrapper(pi); if (tw && tw != pi && (tn != jl_type_typename || jl_typeof(pi) == jl_typeof(tw)) && - jl_types_equal(pi, tw)) { + !jl_has_free_typevars(pi) && jl_types_equal(pi, tw)) { iparams[i] = tw; if (p) jl_gc_wb(p, tw); } @@ -2669,7 +2669,7 @@ jl_vararg_t *jl_wrap_vararg(jl_value_t *t, jl_value_t *n, int check, int nothrow if (valid) { t = normalize_unionalls(t); jl_value_t *tw = extract_wrapper(t); - if (tw && t != tw && jl_types_equal(t, tw)) + if (tw && t != tw && !jl_has_free_typevars(t) && jl_types_equal(t, tw)) t = tw; } } diff --git a/src/julia.h b/src/julia.h index 3a7d594562243..e97b61f85a230 100644 --- a/src/julia.h +++ b/src/julia.h @@ -262,12 +262,13 @@ typedef union __jl_purity_overrides_t { uint16_t ipo_noub : 1; uint16_t ipo_noub_if_noinbounds : 1; uint16_t ipo_consistent_overlay : 1; + uint16_t ipo_nortcall : 1; } overrides; uint16_t bits; } _jl_purity_overrides_t; -#define NUM_EFFECTS_OVERRIDES 10 -#define NUM_IR_FLAGS 12 +#define NUM_EFFECTS_OVERRIDES 11 +#define NUM_IR_FLAGS 14 // This type describes a single function body typedef struct _jl_code_info_t { diff --git a/src/julia_threads.h b/src/julia_threads.h index e69e7fae9b4d9..3a0f7f12bffe5 100644 --- a/src/julia_threads.h +++ b/src/julia_threads.h @@ -275,10 +275,7 @@ typedef struct _jl_tls_states_t { #endif } jl_tls_states_t; -#ifndef JL_LIBRARY_EXPORTS -// deprecated (only for external consumers) JL_DLLEXPORT void *jl_get_ptls_states(void); -#endif // Update codegen version in `ccall.cpp` after changing either `pause` or `wake` #ifdef __MIC__ diff --git a/src/llvm-alloc-helpers.cpp b/src/llvm-alloc-helpers.cpp index 953ecc1830142..9d2fba832839c 100644 --- a/src/llvm-alloc-helpers.cpp +++ b/src/llvm-alloc-helpers.cpp @@ -88,6 +88,8 @@ bool AllocUseInfo::addMemOp(Instruction *inst, unsigned opno, uint32_t offset, memop.isaggr = isa(elty) || isa(elty) || isa(elty); memop.isobjref = hasObjref(elty); auto &field = getField(offset, size, elty); + field.second.hasunboxed |= !hasObjref(elty) || (hasObjref(elty) && !isa(elty)); + if (field.second.hasobjref != memop.isobjref) field.second.multiloc = true; // can't split this field, since it contains a mix of references and bits if (!isstore) @@ -198,6 +200,7 @@ void jl_alloc::runEscapeAnalysis(llvm::CallInst *I, EscapeAnalysisRequiredArgs r auto elty = inst->getType(); required.use_info.has_unknown_objref |= hasObjref(elty); required.use_info.has_unknown_objrefaggr |= hasObjref(elty) && !isa(elty); + required.use_info.has_unknown_unboxed |= !hasObjref(elty) || (hasObjref(elty) && !isa(elty)); required.use_info.hasunknownmem = true; } else if (!required.use_info.addMemOp(inst, 0, cur.offset, inst->getType(), @@ -289,6 +292,7 @@ void jl_alloc::runEscapeAnalysis(llvm::CallInst *I, EscapeAnalysisRequiredArgs r auto elty = storev->getType(); required.use_info.has_unknown_objref |= hasObjref(elty); required.use_info.has_unknown_objrefaggr |= hasObjref(elty) && !isa(elty); + required.use_info.has_unknown_unboxed |= !hasObjref(elty) || (hasObjref(elty) && !isa(elty)); required.use_info.hasunknownmem = true; } else if (!required.use_info.addMemOp(inst, use->getOperandNo(), cur.offset, storev->getType(), @@ -310,10 +314,14 @@ void jl_alloc::runEscapeAnalysis(llvm::CallInst *I, EscapeAnalysisRequiredArgs r } required.use_info.hasload = true; auto storev = isa(inst) ? cast(inst)->getNewValOperand() : cast(inst)->getValOperand(); + Type *elty = storev->getType(); if (cur.offset == UINT32_MAX || !required.use_info.addMemOp(inst, use->getOperandNo(), - cur.offset, storev->getType(), + cur.offset, elty, true, required.DL)) { LLVM_DEBUG(dbgs() << "Atomic inst has unknown offset\n"); + required.use_info.has_unknown_objref |= hasObjref(elty); + required.use_info.has_unknown_objrefaggr |= hasObjref(elty) && !isa(elty); + required.use_info.has_unknown_unboxed |= !hasObjref(elty) || (hasObjref(elty) && !isa(elty)); required.use_info.hasunknownmem = true; } required.use_info.refload = true; diff --git a/src/llvm-alloc-helpers.h b/src/llvm-alloc-helpers.h index 49c3b15332a56..20e9132d10b4c 100644 --- a/src/llvm-alloc-helpers.h +++ b/src/llvm-alloc-helpers.h @@ -46,6 +46,8 @@ namespace jl_alloc { bool hasaggr:1; bool multiloc:1; bool hasload:1; + // The alloc has a unboxed object at this offset. + bool hasunboxed:1; llvm::Type *elty; llvm::SmallVector accesses; Field(uint32_t size, llvm::Type *elty) @@ -54,6 +56,7 @@ namespace jl_alloc { hasaggr(false), multiloc(false), hasload(false), + hasunboxed(false), elty(elty) { } @@ -95,6 +98,9 @@ namespace jl_alloc { // The alloc has an aggregate Julia object reference not in an explicit field. bool has_unknown_objrefaggr:1; + // The alloc has an unboxed object at an unknown offset. + bool has_unknown_unboxed:1; + void reset() { escaped = false; @@ -110,6 +116,7 @@ namespace jl_alloc { allockind = llvm::AllocFnKind::Unknown; has_unknown_objref = false; has_unknown_objrefaggr = false; + has_unknown_unboxed = false; uses.clear(); preserves.clear(); memops.clear(); diff --git a/src/llvm-alloc-opt.cpp b/src/llvm-alloc-opt.cpp index 9638ca7e68c48..c343912b385f9 100644 --- a/src/llvm-alloc-opt.cpp +++ b/src/llvm-alloc-opt.cpp @@ -252,10 +252,12 @@ void Optimizer::optimizeAll() removeAlloc(orig); continue; } + bool has_unboxed = use_info.has_unknown_unboxed; bool has_ref = use_info.has_unknown_objref; bool has_refaggr = use_info.has_unknown_objrefaggr; for (auto memop: use_info.memops) { auto &field = memop.second; + has_unboxed |= field.hasunboxed; if (field.hasobjref) { has_ref = true; // This can be relaxed a little based on hasload @@ -284,6 +286,19 @@ void Optimizer::optimizeAll() splitOnStack(orig); continue; } + // The move to stack code below, if has_ref is set, changes the allocation to an array of jlvalue_t's. This is fine + // if all objects are jlvalue_t's. However, if part of the allocation is an unboxed value (e.g. it is a { float, jlvaluet }), + // then moveToStack will create a [2 x jlvaluet] bitcast to { float, jlvaluet }. + // This later causes the GC rooting pass, to miss-characterize the float as a pointer to a GC value + if (has_unboxed && has_ref) { + REMARK([&]() { + return OptimizationRemarkMissed(DEBUG_TYPE, "Escaped", orig) + << "GC allocation could not be split since it contains both boxed and unboxed values, unable to move to stack " << ore::NV("GC Allocation", orig); + }); + if (use_info.hastypeof) + optimizeTag(orig); + continue; + } REMARK([&](){ return OptimizationRemark(DEBUG_TYPE, "Stack Move Allocation", orig) << "GC allocation moved to stack " << ore::NV("GC Allocation", orig); diff --git a/src/method.c b/src/method.c index 7e6c82b5221b2..7a5d2af49cffd 100644 --- a/src/method.c +++ b/src/method.c @@ -368,6 +368,8 @@ static void jl_code_info_set_ir(jl_code_info_t *li, jl_expr_t *ir) if (noub_if_noinbounds) li->purity.overrides.ipo_noub_if_noinbounds = noub_if_noinbounds; int8_t consistent_overlay = jl_unbox_bool(jl_exprarg(ma, 9)); if (consistent_overlay) li->purity.overrides.ipo_consistent_overlay = consistent_overlay; + int8_t nortcall = jl_unbox_bool(jl_exprarg(ma, 10)); + if (nortcall) li->purity.overrides.ipo_nortcall = nortcall; } } else diff --git a/src/pipeline.cpp b/src/pipeline.cpp index 1cb86a98502d8..5c12e3dad0dd7 100644 --- a/src/pipeline.cpp +++ b/src/pipeline.cpp @@ -618,65 +618,6 @@ static void buildPipeline(ModulePassManager &MPM, PassBuilder *PB, OptimizationL MPM.addPass(AfterOptimizationMarkerPass()); } -struct PipelineConfig { - int Speedup; - int Size; - int lower_intrinsics; - int dump_native; - int external_use; - int llvm_only; - int always_inline; - int enable_early_simplifications; - int enable_early_optimizations; - int enable_scalar_optimizations; - int enable_loop_optimizations; - int enable_vector_pipeline; - int remove_ni; - int cleanup; - int warn_missed_transformations; -}; - -extern "C" JL_DLLEXPORT_CODEGEN void jl_build_newpm_pipeline_impl(void *MPM, void *PB, PipelineConfig* config) JL_NOTSAFEPOINT -{ - OptimizationLevel O; - switch (config->Size) { - case 1: - O = OptimizationLevel::Os; - break; - default: - O = OptimizationLevel::Oz; - break; - case 0: - switch (config->Speedup) { - case 0: - O = OptimizationLevel::O0; - break; - case 1: - O = OptimizationLevel::O1; - break; - case 2: - O = OptimizationLevel::O2; - break; - default: - O = OptimizationLevel::O3; - break; - } - } - buildPipeline(*reinterpret_cast(MPM), reinterpret_cast(PB), O, - OptimizationOptions{!!config->lower_intrinsics, - !!config->dump_native, - !!config->external_use, - !!config->llvm_only, - !!config->always_inline, - !!config->enable_early_simplifications, - !!config->enable_early_optimizations, - !!config->enable_scalar_optimizations, - !!config->enable_loop_optimizations, - !!config->enable_vector_pipeline, - !!config->remove_ni, - !!config->cleanup, - !!config->warn_missed_transformations}); -} #undef JULIA_PASS @@ -862,6 +803,14 @@ static Optional> parseJuliaPip OPTION(dump_native), OPTION(external_use), OPTION(llvm_only), + OPTION(always_inline), + OPTION(enable_early_simplifications), + OPTION(enable_early_optimizations), + OPTION(enable_scalar_optimizations), + OPTION(enable_loop_optimizations), + OPTION(enable_vector_pipeline), + OPTION(remove_ni), + OPTION(cleanup), OPTION(warn_missed_transformations) #undef OPTION }; diff --git a/src/signals-mach.c b/src/signals-mach.c index 56f7c0d7505f4..db241657864c2 100644 --- a/src/signals-mach.c +++ b/src/signals-mach.c @@ -717,16 +717,16 @@ void *mach_profile_listener(void *arg) #endif jl_ptls_t ptls = jl_atomic_load_relaxed(&jl_all_tls_states)[i]; - // store threadid but add 1 as 0 is preserved to indicate end of block + // META_OFFSET_THREADID store threadid but add 1 as 0 is preserved to indicate end of block bt_data_prof[bt_size_cur++].uintptr = ptls->tid + 1; - // store task id (never null) + // META_OFFSET_TASKID store task id (never null) bt_data_prof[bt_size_cur++].jlvalue = (jl_value_t*)jl_atomic_load_relaxed(&ptls->current_task); - // store cpu cycle clock + // META_OFFSET_CPUCYCLECLOCK store cpu cycle clock bt_data_prof[bt_size_cur++].uintptr = cycleclock(); - // store whether thread is sleeping but add 1 as 0 is preserved to indicate end of block + // META_OFFSET_SLEEPSTATE store whether thread is sleeping but add 1 as 0 is preserved to indicate end of block bt_data_prof[bt_size_cur++].uintptr = jl_atomic_load_relaxed(&ptls->sleep_check_state) + 1; // Mark the end of this block with two 0's diff --git a/src/signals-unix.c b/src/signals-unix.c index eb51a5fccfaba..8b79e11863e1f 100644 --- a/src/signals-unix.c +++ b/src/signals-unix.c @@ -914,16 +914,16 @@ static void *signal_listener(void *arg) jl_ptls_t ptls2 = jl_atomic_load_relaxed(&jl_all_tls_states)[i]; - // store threadid but add 1 as 0 is preserved to indicate end of block + // META_OFFSET_THREADID store threadid but add 1 as 0 is preserved to indicate end of block bt_data_prof[bt_size_cur++].uintptr = ptls2->tid + 1; - // store task id (never null) + // META_OFFSET_TASKID store task id (never null) bt_data_prof[bt_size_cur++].jlvalue = (jl_value_t*)jl_atomic_load_relaxed(&ptls2->current_task); - // store cpu cycle clock + // META_OFFSET_CPUCYCLECLOCK store cpu cycle clock bt_data_prof[bt_size_cur++].uintptr = cycleclock(); - // store whether thread is sleeping but add 1 as 0 is preserved to indicate end of block + // META_OFFSET_SLEEPSTATE store whether thread is sleeping but add 1 as 0 is preserved to indicate end of block bt_data_prof[bt_size_cur++].uintptr = jl_atomic_load_relaxed(&ptls2->sleep_check_state) + 1; // Mark the end of this block with two 0's diff --git a/src/signals-win.c b/src/signals-win.c index f763b71e1cf32..a2980a0c063d9 100644 --- a/src/signals-win.c +++ b/src/signals-win.c @@ -420,16 +420,16 @@ static DWORD WINAPI profile_bt( LPVOID lparam ) jl_ptls_t ptls = jl_atomic_load_relaxed(&jl_all_tls_states)[0]; // given only profiling hMainThread - // store threadid but add 1 as 0 is preserved to indicate end of block + // META_OFFSET_THREADID store threadid but add 1 as 0 is preserved to indicate end of block bt_data_prof[bt_size_cur++].uintptr = ptls->tid + 1; - // store task id (never null) + // META_OFFSET_TASKID store task id (never null) bt_data_prof[bt_size_cur++].jlvalue = (jl_value_t*)jl_atomic_load_relaxed(&ptls->current_task); - // store cpu cycle clock + // META_OFFSET_CPUCYCLECLOCK store cpu cycle clock bt_data_prof[bt_size_cur++].uintptr = cycleclock(); - // store whether thread is sleeping but add 1 as 0 is preserved to indicate end of block + // META_OFFSET_SLEEPSTATE store whether thread is sleeping but add 1 as 0 is preserved to indicate end of block bt_data_prof[bt_size_cur++].uintptr = jl_atomic_load_relaxed(&ptls->sleep_check_state) + 1; // Mark the end of this block with two 0's diff --git a/src/subtype.c b/src/subtype.c index 3fdacfbb7a4bb..b12a8ec88e2b7 100644 --- a/src/subtype.c +++ b/src/subtype.c @@ -65,7 +65,6 @@ typedef struct jl_varbinding_t { jl_value_t *lb; jl_value_t *ub; int8_t right; // whether this variable came from the right side of `A <: B` - int8_t occurs; // occurs in any position int8_t occurs_inv; // occurs in invariant position int8_t occurs_cov; // # of occurrences in covariant position int8_t concrete; // 1 if another variable has a constraint forcing this one to be concrete @@ -179,7 +178,7 @@ static int current_env_length(jl_stenv_t *e) typedef struct { int8_t *buf; int rdepth; - int8_t _space[32]; // == 8 * 4 + int8_t _space[24]; // == 8 * 3 jl_gcframe_t gcframe; jl_value_t *roots[24]; // == 8 * 3 } jl_savedenv_t; @@ -208,7 +207,6 @@ static void re_save_env(jl_stenv_t *e, jl_savedenv_t *se, int root) roots[i++] = v->ub; roots[i++] = (jl_value_t*)v->innervars; } - se->buf[j++] = v->occurs; se->buf[j++] = v->occurs_inv; se->buf[j++] = v->occurs_cov; se->buf[j++] = v->max_offset; @@ -243,7 +241,7 @@ static void alloc_env(jl_stenv_t *e, jl_savedenv_t *se, int root) ct->gcstack = &se->gcframe; } } - se->buf = (len > 8 ? (int8_t*)malloc_s(len * 4) : se->_space); + se->buf = (len > 8 ? (int8_t*)malloc_s(len * 3) : se->_space); #ifdef __clang_gcanalyzer__ memset(se->buf, 0, len * 3); #endif @@ -290,7 +288,6 @@ static void restore_env(jl_stenv_t *e, jl_savedenv_t *se, int root) JL_NOTSAFEPO v->ub = roots[i++]; v->innervars = (jl_array_t*)roots[i++]; } - v->occurs = se->buf[j++]; v->occurs_inv = se->buf[j++]; v->occurs_cov = se->buf[j++]; v->max_offset = se->buf[j++]; @@ -302,15 +299,6 @@ static void restore_env(jl_stenv_t *e, jl_savedenv_t *se, int root) JL_NOTSAFEPO memset(&e->envout[e->envidx], 0, (e->envsz - e->envidx)*sizeof(void*)); } -static void clean_occurs(jl_stenv_t *e) -{ - jl_varbinding_t *v = e->vars; - while (v) { - v->occurs = 0; - v = v->prev; - } -} - #define flip_offset(e) ((e)->Loffset *= -1) // type utilities @@ -599,6 +587,8 @@ static jl_value_t *simple_meet(jl_value_t *a, jl_value_t *b, int overesi) static int subtype(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, int param); +#define has_next_union_state(e, R) ((((R) ? &(e)->Runions : &(e)->Lunions)->more) != 0) + static int next_union_state(jl_stenv_t *e, int8_t R) JL_NOTSAFEPOINT { jl_unionstate_t *state = R ? &e->Runions : &e->Lunions; @@ -679,8 +669,6 @@ static int subtype_left_var(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, int par // of determining whether the variable is concrete. static void record_var_occurrence(jl_varbinding_t *vb, jl_stenv_t *e, int param) JL_NOTSAFEPOINT { - if (vb != NULL) - vb->occurs = 1; if (vb != NULL && param) { // saturate counters at 2; we don't need values bigger than that if (param == 2 && e->invdepth > vb->depth0) { @@ -915,7 +903,7 @@ static jl_unionall_t *unalias_unionall(jl_unionall_t *u, jl_stenv_t *e) static int subtype_unionall(jl_value_t *t, jl_unionall_t *u, jl_stenv_t *e, int8_t R, int param) { u = unalias_unionall(u, e); - jl_varbinding_t vb = { u->var, u->var->lb, u->var->ub, R, 0, 0, 0, 0, 0, 0, 0, 0, 0, + jl_varbinding_t vb = { u->var, u->var->lb, u->var->ub, R, 0, 0, 0, 0, 0, 0, 0, 0, e->invdepth, NULL, e->vars }; JL_GC_PUSH4(&u, &vb.lb, &vb.ub, &vb.innervars); e->vars = &vb; @@ -1324,7 +1312,21 @@ static int try_subtype_by_bounds(jl_value_t *a, jl_value_t *b, jl_stenv_t *e); static int subtype(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, int param) { if (jl_is_uniontype(x)) { - if (x == y) return 1; + if (obviously_egal(x, y)) + return 1; + if (e->Runions.depth == 0 && jl_is_typevar(y) && !jl_has_free_typevars(x) && !jl_has_free_typevars(((jl_tvar_t*)y)->ub)) { + // Similar to fast path for repeated elements: if there have been no outer + // unions on the right, and the right side is a typevar, then we can handle the + // typevar first before picking a union element, under the theory that it may + // be easy to match or reject this whole union in comparing and setting the lb + // and ub of the variable binding, without needing to examine each element. + // However, if x contains any free typevars, then each element with a free + // typevar must be handled separately from the union of all elements without + // free typevars, since the typevars presence might lead to those elements + // getting eliminated (omit_bad_union) or degenerate (Union{Ptr{T}, Ptr}) or + // combined (Union{T, S} where {T, S <: T}). + return subtype_var((jl_tvar_t*)y, x, e, 1, param); + } x = pick_union_element(x, e, 0); } if (jl_is_uniontype(y)) { @@ -3312,7 +3314,7 @@ static jl_value_t *intersect_unionall(jl_value_t *t, jl_unionall_t *u, jl_stenv_ { jl_value_t *res = NULL; jl_savedenv_t se; - jl_varbinding_t vb = { u->var, u->var->lb, u->var->ub, R, 0, 0, 0, 0, 0, 0, 0, 0, 0, + jl_varbinding_t vb = { u->var, u->var->lb, u->var->ub, R, 0, 0, 0, 0, 0, 0, 0, 0, e->invdepth, NULL, e->vars }; JL_GC_PUSH4(&res, &vb.lb, &vb.ub, &vb.innervars); save_env(e, &se, 1); @@ -3341,7 +3343,7 @@ static jl_value_t *intersect_unionall(jl_value_t *t, jl_unionall_t *u, jl_stenv_ vb.ub = vb.var->ub; } restore_env(e, &se, vb.constraintkind == 1 ? 1 : 0); - vb.occurs = vb.occurs_cov = vb.occurs_inv = 0; + vb.occurs_cov = vb.occurs_inv = 0; res = intersect_unionall_(t, u, e, R, param, &vb); } } @@ -4042,79 +4044,12 @@ static jl_value_t *intersect(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, int pa return jl_bottom_type; } -static int merge_env(jl_stenv_t *e, jl_savedenv_t *se, int count) +static int merge_env(jl_stenv_t *e, jl_savedenv_t *me, jl_savedenv_t *se, int count) { - if (count == 0) - alloc_env(e, se, 1); - jl_value_t **roots = NULL; - int nroots = 0; - if (se->gcframe.nroots == JL_GC_ENCODE_PUSHARGS(1)) { - jl_svec_t *sv = (jl_svec_t*)se->roots[0]; - assert(jl_is_svec(sv)); - roots = jl_svec_data(sv); - nroots = jl_svec_len(sv); - } - else { - roots = se->roots; - nroots = se->gcframe.nroots >> 2; - } - int m = 0, n = 0; - jl_varbinding_t *v = e->vars; - while (v != NULL) { - if (count == 0) { - // need to initialize this - se->buf[m] = 0; - se->buf[m+1] = 0; - se->buf[m+2] = 0; - se->buf[m+3] = v->max_offset; - } - jl_value_t *b1, *b2; - if (v->occurs) { - // only merge lb/ub if this var occurs. - b1 = roots[n]; - JL_GC_PROMISE_ROOTED(b1); // clang-sagc doesn't know this came from our GC frame - b2 = v->lb; - JL_GC_PROMISE_ROOTED(b2); // clang-sagc doesn't know the fields of this are stack GC roots - roots[n] = b1 ? simple_meet(b1, b2, 0) : b2; - b1 = roots[n+1]; - JL_GC_PROMISE_ROOTED(b1); // clang-sagc doesn't know this came from our GC frame - b2 = v->ub; - JL_GC_PROMISE_ROOTED(b2); // clang-sagc doesn't know the fields of this are stack GC roots - roots[n+1] = b1 ? simple_join(b1, b2) : b2; - // record the meeted vars. - se->buf[m] = 1; - } - // `innervars` might be re-sorted inside `finish_unionall`. - // We'd better always merge it. - b1 = roots[n+2]; - JL_GC_PROMISE_ROOTED(b1); // clang-sagc doesn't know this came from our GC frame - b2 = (jl_value_t*)v->innervars; - JL_GC_PROMISE_ROOTED(b2); // clang-sagc doesn't know the fields of this are stack GC roots - if (b2 && b1 != b2) { - if (b1) - jl_array_ptr_1d_append((jl_array_t*)b1, (jl_array_t*)b2); - else - roots[n+2] = b2; - } - // always merge occurs_inv/cov by max (never decrease) - if (v->occurs_inv > se->buf[m+1]) - se->buf[m+1] = v->occurs_inv; - if (v->occurs_cov > se->buf[m+2]) - se->buf[m+2] = v->occurs_cov; - // always merge max_offset by min - if (!v->intersected && v->max_offset < se->buf[m+3]) - se->buf[m+3] = v->max_offset; - m = m + 4; - n = n + 3; - v = v->prev; + if (count == 0) { + save_env(e, me, 1); + return 1; } - assert(n == nroots); (void)nroots; - return count + 1; -} - -// merge untouched vars' info. -static void final_merge_env(jl_stenv_t *e, jl_savedenv_t *me, jl_savedenv_t *se) -{ jl_value_t **merged = NULL; jl_value_t **saved = NULL; int nroots = 0; @@ -4136,47 +4071,49 @@ static void final_merge_env(jl_stenv_t *e, jl_savedenv_t *me, jl_savedenv_t *se) } assert(nroots == current_env_length(e) * 3); assert(nroots % 3 == 0); - for (int n = 0, m = 0; n < nroots; n += 3, m += 4) { - if (merged[n] == NULL) - merged[n] = saved[n]; - if (merged[n+1] == NULL) - merged[n+1] = saved[n+1]; - jl_value_t *b1, *b2; + int m = 0, n = 0; + jl_varbinding_t *v = e->vars; + while (v != NULL) { + jl_value_t *b0, *b1, *b2; + // merge `lb` + b0 = saved[n]; + b1 = merged[n]; + JL_GC_PROMISE_ROOTED(b1); // clang-sagc doesn't know this came from our GC frame + b2 = v->lb; + JL_GC_PROMISE_ROOTED(b2); // clang-sagc doesn't know the fields of this are stack GC roots + merged[n] = (b1 == b0 || b2 == b0) ? b0 : simple_meet(b1, b2, 0); + // merge `ub` + b0 = saved[n+1]; + b1 = merged[n+1]; + JL_GC_PROMISE_ROOTED(b1); // clang-sagc doesn't know this came from our GC frame + b2 = v->ub; + JL_GC_PROMISE_ROOTED(b2); // clang-sagc doesn't know the fields of this are stack GC roots + merged[n+1] = (b1 == b0 || b2 == b0) ? b0 : simple_join(b1, b2); + // merge `innervars` b1 = merged[n+2]; JL_GC_PROMISE_ROOTED(b1); // clang-sagc doesn't know this came from our GC frame - b2 = saved[n+2]; - JL_GC_PROMISE_ROOTED(b2); // clang-sagc doesn't know this came from our GC frame + b2 = (jl_value_t*)v->innervars; + JL_GC_PROMISE_ROOTED(b2); // clang-sagc doesn't know the fields of this are stack GC roots if (b2 && b1 != b2) { if (b1) jl_array_ptr_1d_append((jl_array_t*)b1, (jl_array_t*)b2); else merged[n+2] = b2; } - me->buf[m] |= se->buf[m]; - } -} - -static void expand_local_env(jl_stenv_t *e, jl_value_t *res) -{ - jl_varbinding_t *v = e->vars; - // Here we pull in some typevar missed in fastpath. - while (v != NULL) { - v->occurs = v->occurs || jl_has_typevar(res, v->var); - assert(v->occurs == 0 || v->occurs == 1); - v = v->prev; - } - v = e->vars; - while (v != NULL) { - if (v->occurs == 1) { - jl_varbinding_t *v2 = e->vars; - while (v2 != NULL) { - if (v2 != v && v2->occurs == 0) - v2->occurs = -(jl_has_typevar(v->lb, v2->var) || jl_has_typevar(v->ub, v2->var)); - v2 = v2->prev; - } - } + // merge occurs_inv/cov by max (never decrease) + if (v->occurs_inv > me->buf[m]) + me->buf[m] = v->occurs_inv; + if (v->occurs_cov > me->buf[m+1]) + me->buf[m+1] = v->occurs_cov; + // merge max_offset by min + if (!v->intersected && v->max_offset < me->buf[m+2]) + me->buf[m+2] = v->max_offset; + m = m + 3; + n = n + 3; v = v->prev; } + assert(n == nroots); (void)nroots; + return count + 1; } static jl_value_t *intersect_all(jl_value_t *x, jl_value_t *y, jl_stenv_t *e) @@ -4189,12 +4126,9 @@ static jl_value_t *intersect_all(jl_value_t *x, jl_value_t *y, jl_stenv_t *e) jl_savedenv_t se, me; save_env(e, &se, 1); int niter = 0, total_iter = 0; - clean_occurs(e); is[0] = intersect(x, y, e, 0); // root - if (is[0] != jl_bottom_type) { - expand_local_env(e, is[0]); - niter = merge_env(e, &me, niter); - } + if (is[0] != jl_bottom_type) + niter = merge_env(e, &me, &se, niter); restore_env(e, &se, 1); while (next_union_state(e, 1)) { if (e->emptiness_only && is[0] != jl_bottom_type) @@ -4202,12 +4136,9 @@ static jl_value_t *intersect_all(jl_value_t *x, jl_value_t *y, jl_stenv_t *e) e->Runions.depth = 0; e->Runions.more = 0; - clean_occurs(e); is[1] = intersect(x, y, e, 0); - if (is[1] != jl_bottom_type) { - expand_local_env(e, is[1]); - niter = merge_env(e, &me, niter); - } + if (is[1] != jl_bottom_type) + niter = merge_env(e, &me, &se, niter); restore_env(e, &se, 1); if (is[0] == jl_bottom_type) is[0] = is[1]; @@ -4216,13 +4147,18 @@ static jl_value_t *intersect_all(jl_value_t *x, jl_value_t *y, jl_stenv_t *e) is[0] = jl_type_union(is, 2); } total_iter++; - if (niter > 4 || total_iter > 400000) { + if (has_next_union_state(e, 1) && (niter > 4 || total_iter > 400000)) { is[0] = y; + // we give up precise intersection here, just restore the saved env + restore_env(e, &se, 1); + if (niter > 0) { + free_env(&me); + niter = 0; + } break; } } if (niter) { - final_merge_env(e, &me, &se); restore_env(e, &me, 1); free_env(&me); } @@ -4707,7 +4643,7 @@ static jl_value_t *_widen_diagonal(jl_value_t *t, jl_varbinding_t *troot) { static jl_value_t *widen_diagonal(jl_value_t *t, jl_unionall_t *u, jl_varbinding_t *troot) { - jl_varbinding_t vb = { u->var, NULL, NULL, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, troot }; + jl_varbinding_t vb = { u->var, NULL, NULL, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, troot }; jl_value_t *nt; JL_GC_PUSH2(&vb.innervars, &nt); if (jl_is_unionall(u->body)) @@ -4730,6 +4666,56 @@ JL_DLLEXPORT jl_value_t *jl_widen_diagonal(jl_value_t *t, jl_unionall_t *ua) } // specificity comparison +static int count_missing_wrap(jl_value_t *x, jl_typeenv_t *env) +{ + if (!jl_has_free_typevars(x)) + return 0; + jl_typeenv_t *wrapped = NULL; + int count = 0; + for (jl_typeenv_t *env2 = env; env2 != NULL; env2 = env2->prev) { + int need_wrap = 0; + for (jl_typeenv_t *env3 = wrapped; env3 != NULL && need_wrap == 0; env3 = env3->prev) { + if (env3->var == env2->var) + need_wrap = -1; + else if (jl_has_typevar(env3->var->lb, env2->var) || jl_has_typevar(env3->var->ub, env2->var)) + need_wrap = 1; + } + need_wrap = need_wrap == 0 ? jl_has_typevar(x, env2->var) : + need_wrap == -1 ? 0 : 1; + if (need_wrap) { + count++; + jl_typeenv_t *newenv = (jl_typeenv_t*)alloca(sizeof(jl_typeenv_t)); + newenv->var = env2->var; + newenv->val = NULL; + newenv->prev = wrapped; + wrapped = newenv; + } + } + return count; +} + +static int obvious_subtype_msp(jl_value_t *x, jl_value_t *y, jl_value_t *y0, int *subtype, int wrapx, int wrapy) +{ + if (wrapx != 0 || wrapy != 0) { + int wrap_count = wrapx - wrapy; + while (wrap_count > 0 && jl_is_unionall(y)) + { + y = ((jl_unionall_t*)y)->body; + wrap_count--; + } + while (wrap_count < 0 && jl_is_unionall(x)) + { + x = ((jl_unionall_t*)x)->body; + wrap_count++; + } + if (wrap_count > 0) { + if (obvious_subtype(jl_unwrap_unionall(x), y, y0, subtype) && !*subtype) + return 1; + return 0; + } + } + return obvious_subtype(x, y, y0, subtype); +} static int eq_msp(jl_value_t *a, jl_value_t *b, jl_value_t *a0, jl_value_t *b0, jl_typeenv_t *env) { @@ -4752,12 +4738,14 @@ static int eq_msp(jl_value_t *a, jl_value_t *b, jl_value_t *a0, jl_value_t *b0, a = b; b = temp; } + int wrapa = count_missing_wrap(a, env); + int wrapb = count_missing_wrap(b, env); // first check if a <: b has an obvious answer int subtype_ab = 2; if (b == (jl_value_t*)jl_any_type || a == jl_bottom_type) { subtype_ab = 1; } - else if (obvious_subtype(a, b, b0, &subtype_ab)) { + else if (obvious_subtype_msp(a, b, b0, &subtype_ab, wrapa, wrapb)) { #ifdef NDEBUG if (subtype_ab == 0) return 0; @@ -4771,7 +4759,7 @@ static int eq_msp(jl_value_t *a, jl_value_t *b, jl_value_t *a0, jl_value_t *b0, if (a == (jl_value_t*)jl_any_type || b == jl_bottom_type) { subtype_ba = 1; } - else if (obvious_subtype(b, a, a0, &subtype_ba)) { + else if (obvious_subtype_msp(b, a, a0, &subtype_ba, wrapb, wrapa)) { #ifdef NDEBUG if (subtype_ba == 0) return 0; @@ -4836,7 +4824,9 @@ static int sub_msp(jl_value_t *x, jl_value_t *y, jl_value_t *y0, jl_typeenv_t *e return 1; } int obvious_sub = 2; - if (obvious_subtype(x, y, y0, &obvious_sub)) { + int wrapx = count_missing_wrap(x, env); + int wrapy = count_missing_wrap(y, env); + if (obvious_subtype_msp(x, y, y0, &obvious_sub, wrapx, wrapy)) { #ifdef NDEBUG return obvious_sub; #endif diff --git a/stdlib/Dates/docs/src/index.md b/stdlib/Dates/docs/src/index.md index 35ec6771efc55..6517ecae62196 100644 --- a/stdlib/Dates/docs/src/index.md +++ b/stdlib/Dates/docs/src/index.md @@ -684,9 +684,9 @@ value in the days field is uncertain. See the [API reference](@ref stdlib-dates-api) for additional information on methods exported from the `Dates` module. -# [API reference](@id stdlib-dates-api) +## [API reference](@id stdlib-dates-api) -## Dates and Time Types +### Dates and Time Types ```@docs Dates.Period @@ -701,7 +701,7 @@ Dates.TimeZone Dates.UTC ``` -## Dates Functions +### Dates Functions ```@docs Dates.DateTime(::Int64, ::Int64, ::Int64, ::Int64, ::Int64, ::Int64, ::Int64) @@ -730,7 +730,7 @@ Dates.now(::Type{Dates.UTC}) Base.eps(::Union{Type{DateTime}, Type{Date}, Type{Time}, TimeType}) ``` -### Accessor Functions +#### Accessor Functions ```@docs Dates.year @@ -758,7 +758,7 @@ Dates.monthday Dates.yearmonthday ``` -### Query Functions +#### Query Functions ```@docs Dates.dayname @@ -777,7 +777,7 @@ Dates.quarterofyear Dates.dayofquarter ``` -### Adjuster Functions +#### Adjuster Functions ```@docs Base.trunc(::Dates.TimeType, ::Type{Dates.Period}) @@ -797,7 +797,7 @@ Dates.tonext(::Function, ::Dates.TimeType) Dates.toprev(::Function, ::Dates.TimeType) ``` -### Periods +#### Periods ```@docs Dates.Period(::Any) @@ -808,7 +808,7 @@ Dates.default Dates.periods ``` -### Rounding Functions +#### Rounding Functions `Date` and `DateTime` values can be rounded to a specified resolution (e.g., 1 month or 15 minutes) with `floor`, `ceil`, or `round`. @@ -837,7 +837,7 @@ Dates.date2epochdays Dates.datetime2epochms ``` -### Conversion Functions +#### Conversion Functions ```@docs Dates.today diff --git a/stdlib/Dates/src/types.jl b/stdlib/Dates/src/types.jl index e1f7f900bff51..1978864b92554 100644 --- a/stdlib/Dates/src/types.jl +++ b/stdlib/Dates/src/types.jl @@ -203,7 +203,7 @@ function totaldays(y, m, d) end # If the year is divisible by 4, except for every 100 years, except for every 400 years -isleapyear(y) = (y % 4 == 0) && ((y % 100 != 0) || (y % 400 == 0)) +isleapyear(y::Integer) = (y % 4 == 0) && ((y % 100 != 0) || (y % 400 == 0)) # Number of days in month const DAYSINMONTH = (31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31) diff --git a/stdlib/Dates/test/types.jl b/stdlib/Dates/test/types.jl index 35a793867dc5a..f5284b376ca4a 100644 --- a/stdlib/Dates/test/types.jl +++ b/stdlib/Dates/test/types.jl @@ -41,6 +41,7 @@ end @test Dates.isleapyear(-1) == false @test Dates.isleapyear(4) == true @test Dates.isleapyear(-4) == true + @test_throws MethodError Dates.isleapyear(Dates.Year(1992)) end # Create "test" check manually y = Dates.Year(1) diff --git a/stdlib/LinearAlgebra/src/bidiag.jl b/stdlib/LinearAlgebra/src/bidiag.jl index 91e41855bcbc6..0144a575ccfdf 100644 --- a/stdlib/LinearAlgebra/src/bidiag.jl +++ b/stdlib/LinearAlgebra/src/bidiag.jl @@ -179,7 +179,7 @@ end @inbounds A.ev[j] = x elseif !iszero(x) throw(ArgumentError(LazyString(lazy"cannot set entry ($i, $j) off the ", - istriu(A) ? "upper" : "lower", " bidiagonal band to a nonzero value ", x))) + A.uplo == 'U' ? "upper" : "lower", " bidiagonal band to a nonzero value ", x))) end return x end @@ -435,8 +435,76 @@ const BiTri = Union{Bidiagonal,Tridiagonal} @inline _mul!(C::AbstractMatrix, A::AbstractMatrix, B::BandedMatrix, alpha::Number, beta::Number) = _mul!(C, A, B, MulAddMul(alpha, beta)) @inline _mul!(C::AbstractMatrix, A::BandedMatrix, B::BandedMatrix, alpha::Number, beta::Number) = _mul!(C, A, B, MulAddMul(alpha, beta)) -lmul!(A::Bidiagonal, B::AbstractVecOrMat) = @inline _mul!(B, A, B, MulAddMul()) -rmul!(B::AbstractMatrix, A::Bidiagonal) = @inline _mul!(B, B, A, MulAddMul()) +# B .= A * B +function lmul!(A::Bidiagonal, B::AbstractVecOrMat) + _muldiag_size_check(A, B) + (; dv, ev) = A + if A.uplo == 'U' + for k in axes(B,2) + for i in axes(ev,1) + B[i,k] = dv[i] * B[i,k] + ev[i] * B[i+1,k] + end + B[end,k] = dv[end] * B[end,k] + end + else + for k in axes(B,2) + for i in reverse(axes(dv,1)[2:end]) + B[i,k] = dv[i] * B[i,k] + ev[i-1] * B[i-1,k] + end + B[1,k] = dv[1] * B[1,k] + end + end + return B +end +# B .= D * B +function lmul!(D::Diagonal, B::Bidiagonal) + _muldiag_size_check(D, B) + (; dv, ev) = B + isL = B.uplo == 'L' + dv[1] = D.diag[1] * dv[1] + for i in axes(ev,1) + ev[i] = D.diag[i + isL] * ev[i] + dv[i+1] = D.diag[i+1] * dv[i+1] + end + return B +end +# B .= B * A +function rmul!(B::AbstractMatrix, A::Bidiagonal) + _muldiag_size_check(A, B) + (; dv, ev) = A + if A.uplo == 'U' + for k in reverse(axes(dv,1)[2:end]) + for i in axes(B,1) + B[i,k] = B[i,k] * dv[k] + B[i,k-1] * ev[k-1] + end + end + for i in axes(B,1) + B[i,1] *= dv[1] + end + else + for k in axes(ev,1) + for i in axes(B,1) + B[i,k] = B[i,k] * dv[k] + B[i,k+1] * ev[k] + end + end + for i in axes(B,1) + B[i,end] *= dv[end] + end + end + return B +end +# B .= B * D +function rmul!(B::Bidiagonal, D::Diagonal) + _muldiag_size_check(B, D) + (; dv, ev) = B + isU = B.uplo == 'U' + dv[1] *= D.diag[1] + for i in axes(ev,1) + ev[i] *= D.diag[i + isU] + dv[i+1] *= D.diag[i+1] + end + return B +end function check_A_mul_B!_sizes(C, A, B) mA, nA = size(A) diff --git a/stdlib/LinearAlgebra/src/diagonal.jl b/stdlib/LinearAlgebra/src/diagonal.jl index 6ee4f1279b4fd..d27da450205f7 100644 --- a/stdlib/LinearAlgebra/src/diagonal.jl +++ b/stdlib/LinearAlgebra/src/diagonal.jl @@ -251,10 +251,10 @@ end (-)(Da::Diagonal, Db::Diagonal) = Diagonal(Da.diag - Db.diag) for f in (:+, :-) - @eval function $f(D::Diagonal, S::Symmetric) + @eval function $f(D::Diagonal{<:Number}, S::Symmetric) return Symmetric($f(D, S.data), sym_uplo(S.uplo)) end - @eval function $f(S::Symmetric, D::Diagonal) + @eval function $f(S::Symmetric, D::Diagonal{<:Number}) return Symmetric($f(S.data, D), sym_uplo(S.uplo)) end @eval function $f(D::Diagonal{<:Real}, H::Hermitian) @@ -310,8 +310,49 @@ function (*)(D::Diagonal, V::AbstractVector) return D.diag .* V end -rmul!(A::AbstractMatrix, D::Diagonal) = @inline mul!(A, A, D) -lmul!(D::Diagonal, B::AbstractVecOrMat) = @inline mul!(B, D, B) +function rmul!(A::AbstractMatrix, D::Diagonal) + _muldiag_size_check(A, D) + for I in CartesianIndices(A) + row, col = Tuple(I) + @inbounds A[row, col] *= D.diag[col] + end + return A +end +# T .= T * D +function rmul!(T::Tridiagonal, D::Diagonal) + _muldiag_size_check(T, D) + (; dl, d, du) = T + d[1] *= D.diag[1] + for i in axes(dl,1) + dl[i] *= D.diag[i] + du[i] *= D.diag[i+1] + d[i+1] *= D.diag[i+1] + end + return T +end + +function lmul!(D::Diagonal, B::AbstractVecOrMat) + _muldiag_size_check(D, B) + for I in CartesianIndices(B) + row = I[1] + @inbounds B[I] = D.diag[row] * B[I] + end + return B +end + +# in-place multiplication with a diagonal +# T .= D * T +function lmul!(D::Diagonal, T::Tridiagonal) + _muldiag_size_check(D, T) + (; dl, d, du) = T + d[1] = D.diag[1] * d[1] + for i in axes(dl,1) + dl[i] = D.diag[i+1] * dl[i] + du[i] = D.diag[i] * du[i] + d[i+1] = D.diag[i+1] * d[i+1] + end + return T +end function __muldiag!(out, D::Diagonal, B, _add::MulAddMul{ais1,bis0}) where {ais1,bis0} require_one_based_indexing(out, B) diff --git a/stdlib/LinearAlgebra/src/generic.jl b/stdlib/LinearAlgebra/src/generic.jl index c2144bf85d024..295f91878ee74 100644 --- a/stdlib/LinearAlgebra/src/generic.jl +++ b/stdlib/LinearAlgebra/src/generic.jl @@ -1607,7 +1607,7 @@ end """ reflectorApply!(x, τ, A) -Multiplies `A` in-place by a Householder reflection on the left. It is equivalent to `A .= (I - τ*[1; x] * [1; x]')*A`. +Multiplies `A` in-place by a Householder reflection on the left. It is equivalent to `A .= (I - conj(τ)*[1; x[2:end]]*[1; x[2:end]]')*A`. """ @inline function reflectorApply!(x::AbstractVector, τ::Number, A::AbstractVecOrMat) require_one_based_indexing(x) diff --git a/stdlib/LinearAlgebra/src/lapack.jl b/stdlib/LinearAlgebra/src/lapack.jl index 30307d365251b..1746254b92293 100644 --- a/stdlib/LinearAlgebra/src/lapack.jl +++ b/stdlib/LinearAlgebra/src/lapack.jl @@ -5329,7 +5329,7 @@ for (syev, syevr, syevd, sygvd, elty) in # INTEGER INFO, LDA, LWORK, N # * .. Array Arguments .. # DOUBLE PRECISION A( LDA, * ), W( * ), WORK( * ) - function syev!(jobz::AbstractChar, uplo::AbstractChar, A::AbstractMatrix{$elty}) + Base.@constprop :aggressive function syev!(jobz::AbstractChar, uplo::AbstractChar, A::AbstractMatrix{$elty}) require_one_based_indexing(A) @chkvalidparam 1 jobz ('N', 'V') chkuplo(uplo) @@ -5429,7 +5429,7 @@ for (syev, syevr, syevd, sygvd, elty) in # * .. Array Arguments .. # INTEGER IWORK( * ) # DOUBLE PRECISION A( LDA, * ), W( * ), WORK( * ) - function syevd!(jobz::AbstractChar, uplo::AbstractChar, A::AbstractMatrix{$elty}) + Base.@constprop :aggressive function syevd!(jobz::AbstractChar, uplo::AbstractChar, A::AbstractMatrix{$elty}) require_one_based_indexing(A) @chkvalidparam 1 jobz ('N', 'V') chkstride1(A) @@ -5526,7 +5526,7 @@ for (syev, syevr, syevd, sygvd, elty, relty) in # * .. Array Arguments .. # DOUBLE PRECISION RWORK( * ), W( * ) # COMPLEX*16 A( LDA, * ), WORK( * ) - function syev!(jobz::AbstractChar, uplo::AbstractChar, A::AbstractMatrix{$elty}) + Base.@constprop :aggressive function syev!(jobz::AbstractChar, uplo::AbstractChar, A::AbstractMatrix{$elty}) require_one_based_indexing(A) @chkvalidparam 1 jobz ('N', 'V') chkstride1(A) @@ -5639,7 +5639,7 @@ for (syev, syevr, syevd, sygvd, elty, relty) in # INTEGER IWORK( * ) # DOUBLE PRECISION RWORK( * ) # COMPLEX*16 A( LDA, * ), WORK( * ) - function syevd!(jobz::AbstractChar, uplo::AbstractChar, A::AbstractMatrix{$elty}) + Base.@constprop :aggressive function syevd!(jobz::AbstractChar, uplo::AbstractChar, A::AbstractMatrix{$elty}) require_one_based_indexing(A) @chkvalidparam 1 jobz ('N', 'V') chkstride1(A) diff --git a/stdlib/LinearAlgebra/src/lbt.jl b/stdlib/LinearAlgebra/src/lbt.jl index 02b4411566290..606ddedbe1343 100644 --- a/stdlib/LinearAlgebra/src/lbt.jl +++ b/stdlib/LinearAlgebra/src/lbt.jl @@ -284,6 +284,25 @@ function lbt_find_backing_library(symbol_name, interface::Symbol; end +""" + lbt_forwarded_funcs(config::LBTConfig, lib::LBTLibraryInfo) + +Given a backing library `lib`, return the list of all functions that are +forwarded to that library, as a vector of `String`s. +""" +function lbt_forwarded_funcs(config::LBTConfig, lib::LBTLibraryInfo) + forwarded_funcs = String[] + for (symbol_idx, symbol) in enumerate(config.exported_symbols) + forward_byte_offset = div(symbol_idx - 1, 8) + forward_byte_mask = 1 << mod(symbol_idx - 1, 8) + if lib.active_forwards[forward_byte_offset+1] & forward_byte_mask != 0x00 + push!(forwarded_funcs, symbol) + end + end + return forwarded_funcs +end + + ## NOTE: Manually setting forwards is referred to as the 'footgun API'. It allows truly ## bizarre and complex setups to be created. If you run into strange errors while using ## it, the first thing you should ask yourself is whether you've set things up properly. diff --git a/stdlib/LinearAlgebra/src/symmetric.jl b/stdlib/LinearAlgebra/src/symmetric.jl index 07240fb9afb22..6f778629b51b7 100644 --- a/stdlib/LinearAlgebra/src/symmetric.jl +++ b/stdlib/LinearAlgebra/src/symmetric.jl @@ -12,7 +12,7 @@ struct Symmetric{T,S<:AbstractMatrix{<:T}} <: AbstractMatrix{T} end end """ - Symmetric(A, uplo=:U) + Symmetric(A::AbstractMatrix, uplo::Symbol=:U) Construct a `Symmetric` view of the upper (if `uplo = :U`) or lower (if `uplo = :L`) triangle of the matrix `A`. @@ -63,7 +63,7 @@ function Symmetric(A::AbstractMatrix, uplo::Symbol=:U) end """ - symmetric(A, uplo=:U) + symmetric(A, uplo::Symbol=:U) Construct a symmetric view of `A`. If `A` is a matrix, `uplo` controls whether the upper (if `uplo = :U`) or lower (if `uplo = :L`) triangle of `A` is used to implicitly fill the @@ -105,7 +105,7 @@ struct Hermitian{T,S<:AbstractMatrix{<:T}} <: AbstractMatrix{T} end end """ - Hermitian(A, uplo=:U) + Hermitian(A::AbstractMatrix, uplo::Symbol=:U) Construct a `Hermitian` view of the upper (if `uplo = :U`) or lower (if `uplo = :L`) triangle of the matrix `A`. @@ -153,7 +153,7 @@ function Hermitian(A::AbstractMatrix, uplo::Symbol=:U) end """ - hermitian(A, uplo=:U) + hermitian(A, uplo::Symbol=:U) Construct a hermitian view of `A`. If `A` is a matrix, `uplo` controls whether the upper (if `uplo = :U`) or lower (if `uplo = :L`) triangle of `A` is used to implicitly fill the @@ -422,8 +422,8 @@ Base.copy(A::Adjoint{<:Any,<:Symmetric}) = Base.copy(A::Transpose{<:Any,<:Hermitian}) = Hermitian(copy(transpose(A.parent.data)), ifelse(A.parent.uplo == 'U', :L, :U)) -tr(A::Symmetric) = tr(A.data) # to avoid AbstractMatrix fallback (incl. allocations) -tr(A::Hermitian) = real(tr(A.data)) +tr(A::Symmetric{<:Number}) = tr(A.data) # to avoid AbstractMatrix fallback (incl. allocations) +tr(A::Hermitian{<:Number}) = real(tr(A.data)) Base.conj(A::Symmetric) = Symmetric(parentof_applytri(conj, A), sym_uplo(A.uplo)) Base.conj(A::Hermitian) = Hermitian(parentof_applytri(conj, A), sym_uplo(A.uplo)) @@ -844,7 +844,7 @@ function cbrt(A::HermOrSym{<:Real}) end """ - hermitianpart(A, uplo=:U) -> Hermitian + hermitianpart(A::AbstractMatrix, uplo::Symbol=:U) -> Hermitian Return the Hermitian part of the square matrix `A`, defined as `(A + A') / 2`, as a [`Hermitian`](@ref) matrix. For real matrices `A`, this is also known as the symmetric part @@ -860,7 +860,7 @@ See also [`hermitianpart!`](@ref) for the corresponding in-place operation. hermitianpart(A::AbstractMatrix, uplo::Symbol=:U) = Hermitian(_hermitianpart(A), uplo) """ - hermitianpart!(A, uplo=:U) -> Hermitian + hermitianpart!(A::AbstractMatrix, uplo::Symbol=:U) -> Hermitian Overwrite the square matrix `A` in-place with its Hermitian part `(A + A') / 2`, and return [`Hermitian(A, uplo)`](@ref). For real matrices `A`, this is also known as the symmetric diff --git a/stdlib/LinearAlgebra/src/triangular.jl b/stdlib/LinearAlgebra/src/triangular.jl index 6f204959448d4..0cd5a49a0956d 100644 --- a/stdlib/LinearAlgebra/src/triangular.jl +++ b/stdlib/LinearAlgebra/src/triangular.jl @@ -643,6 +643,43 @@ function _triscale!(A::LowerOrUnitLowerTriangular, c::Number, B::UnitLowerTriang return A end +function _trirdiv!(A::UpperTriangular, B::UpperOrUnitUpperTriangular, c::Number) + n = checksize1(A, B) + for j in 1:n + for i in 1:j + @inbounds A[i, j] = B[i, j] / c + end + end + return A +end +function _trirdiv!(A::LowerTriangular, B::LowerOrUnitLowerTriangular, c::Number) + n = checksize1(A, B) + for j in 1:n + for i in j:n + @inbounds A[i, j] = B[i, j] / c + end + end + return A +end +function _trildiv!(A::UpperTriangular, c::Number, B::UpperOrUnitUpperTriangular) + n = checksize1(A, B) + for j in 1:n + for i in 1:j + @inbounds A[i, j] = c \ B[i, j] + end + end + return A +end +function _trildiv!(A::LowerTriangular, c::Number, B::LowerOrUnitLowerTriangular) + n = checksize1(A, B) + for j in 1:n + for i in j:n + @inbounds A[i, j] = c \ B[i, j] + end + end + return A +end + rmul!(A::UpperOrLowerTriangular, c::Number) = @inline _triscale!(A, A, c, MulAddMul()) lmul!(c::Number, A::UpperOrLowerTriangular) = @inline _triscale!(A, c, A, MulAddMul()) @@ -964,7 +1001,11 @@ for (t, unitt) in ((UpperTriangular, UnitUpperTriangular), tstrided = t{<:Any, <:StridedMaybeAdjOrTransMat} @eval begin (*)(A::$t, x::Number) = $t(A.data*x) - (*)(A::$tstrided, x::Number) = A .* x + function (*)(A::$tstrided, x::Number) + eltype_dest = promote_op(*, eltype(A), typeof(x)) + dest = $t(similar(parent(A), eltype_dest)) + _triscale!(dest, x, A, MulAddMul()) + end function (*)(A::$unitt, x::Number) B = $t(A.data)*x @@ -975,7 +1016,11 @@ for (t, unitt) in ((UpperTriangular, UnitUpperTriangular), end (*)(x::Number, A::$t) = $t(x*A.data) - (*)(x::Number, A::$tstrided) = x .* A + function (*)(x::Number, A::$tstrided) + eltype_dest = promote_op(*, typeof(x), eltype(A)) + dest = $t(similar(parent(A), eltype_dest)) + _triscale!(dest, x, A, MulAddMul()) + end function (*)(x::Number, A::$unitt) B = x*$t(A.data) @@ -986,7 +1031,11 @@ for (t, unitt) in ((UpperTriangular, UnitUpperTriangular), end (/)(A::$t, x::Number) = $t(A.data/x) - (/)(A::$tstrided, x::Number) = A ./ x + function (/)(A::$tstrided, x::Number) + eltype_dest = promote_op(/, eltype(A), typeof(x)) + dest = $t(similar(parent(A), eltype_dest)) + _trirdiv!(dest, A, x) + end function (/)(A::$unitt, x::Number) B = $t(A.data)/x @@ -998,7 +1047,11 @@ for (t, unitt) in ((UpperTriangular, UnitUpperTriangular), end (\)(x::Number, A::$t) = $t(x\A.data) - (\)(x::Number, A::$tstrided) = x .\ A + function (\)(x::Number, A::$tstrided) + eltype_dest = promote_op(\, typeof(x), eltype(A)) + dest = $t(similar(parent(A), eltype_dest)) + _trildiv!(dest, x, A) + end function (\)(x::Number, A::$unitt) B = x\$t(A.data) diff --git a/stdlib/LinearAlgebra/src/tridiag.jl b/stdlib/LinearAlgebra/src/tridiag.jl index 497e265b280bc..bdedcd1b5e023 100644 --- a/stdlib/LinearAlgebra/src/tridiag.jl +++ b/stdlib/LinearAlgebra/src/tridiag.jl @@ -174,7 +174,7 @@ Base.copy(S::Adjoint{<:Any,<:SymTridiagonal}) = SymTridiagonal(map(x -> copy.(ad ishermitian(S::SymTridiagonal) = isreal(S.dv) && isreal(_evview(S)) issymmetric(S::SymTridiagonal) = true -tr(S::SymTridiagonal) = sum(S.dv) +tr(S::SymTridiagonal) = sum(symmetric, S.dv) @noinline function throw_diag_outofboundserror(n, sz) sz1, sz2 = sz diff --git a/stdlib/LinearAlgebra/test/bidiag.jl b/stdlib/LinearAlgebra/test/bidiag.jl index 8d48c42a7f7ea..614b1662f3627 100644 --- a/stdlib/LinearAlgebra/test/bidiag.jl +++ b/stdlib/LinearAlgebra/test/bidiag.jl @@ -884,4 +884,44 @@ end @test mul!(C1, B, sv, 1, 2) == mul!(C2, B, v, 1 ,2) end +@testset "rmul!/lmul! with banded matrices" begin + dv, ev = rand(4), rand(3) + for A in (Bidiagonal(dv, ev, :U), Bidiagonal(dv, ev, :L)) + @testset "$(nameof(typeof(B)))" for B in ( + Bidiagonal(dv, ev, :U), + Bidiagonal(dv, ev, :L), + Diagonal(dv) + ) + @test_throws ArgumentError rmul!(B, A) + @test_throws ArgumentError lmul!(A, B) + end + D = Diagonal(dv) + @test rmul!(copy(A), D) ≈ A * D + @test lmul!(D, copy(A)) ≈ D * A + end + @testset "non-commutative" begin + S32 = SizedArrays.SizedArray{(3,2)}(rand(3,2)) + S33 = SizedArrays.SizedArray{(3,3)}(rand(3,3)) + S22 = SizedArrays.SizedArray{(2,2)}(rand(2,2)) + for uplo in (:L, :U) + B = Bidiagonal(fill(S32, 4), fill(S32, 3), uplo) + D = Diagonal(fill(S22, size(B,2))) + @test rmul!(copy(B), D) ≈ B * D + D = Diagonal(fill(S33, size(B,1))) + @test lmul!(D, copy(B)) ≈ D * B + end + + B = Bidiagonal(fill(S33, 4), fill(S33, 3), :U) + D = Diagonal(fill(S32, 4)) + @test lmul!(B, Array(D)) ≈ B * D + B = Bidiagonal(fill(S22, 4), fill(S22, 3), :U) + @test rmul!(Array(D), B) ≈ D * B + end +end + +@testset "off-band indexing error" begin + B = Bidiagonal(Vector{BigInt}(undef, 4), Vector{BigInt}(undef,3), :L) + @test_throws "cannot set entry" B[1,2] = 4 +end + end # module TestBidiagonal diff --git a/stdlib/LinearAlgebra/test/diagonal.jl b/stdlib/LinearAlgebra/test/diagonal.jl index 3087e87f63415..c20c63b5b3477 100644 --- a/stdlib/LinearAlgebra/test/diagonal.jl +++ b/stdlib/LinearAlgebra/test/diagonal.jl @@ -1288,4 +1288,28 @@ end @test yadj == x' end +@testset "rmul!/lmul! with banded matrices" begin + @testset "$(nameof(typeof(B)))" for B in ( + Bidiagonal(rand(4), rand(3), :L), + Tridiagonal(rand(3), rand(4), rand(3)) + ) + BA = Array(B) + D = Diagonal(rand(size(B,1))) + DA = Array(D) + @test rmul!(copy(B), D) ≈ B * D ≈ BA * DA + @test lmul!(D, copy(B)) ≈ D * B ≈ DA * BA + end +end +@testset "+/- with block Symmetric/Hermitian" begin + for p in ([1 2; 3 4], [1 2+im; 2-im 4+2im]) + m = SizedArrays.SizedArray{(2,2)}(p) + D = Diagonal(fill(m, 2)) + for T in (Symmetric, Hermitian) + S = T(fill(m, 2, 2)) + @test D + S == Array(D) + Array(S) + @test S + D == Array(S) + Array(D) + end + end +end + end # module TestDiagonal diff --git a/stdlib/LinearAlgebra/test/lapack.jl b/stdlib/LinearAlgebra/test/lapack.jl index 000438a004b23..fc7eab44e1078 100644 --- a/stdlib/LinearAlgebra/test/lapack.jl +++ b/stdlib/LinearAlgebra/test/lapack.jl @@ -879,4 +879,14 @@ a = zeros(2,0), zeros(0) @test_throws DimensionMismatch LinearAlgebra.LAPACK.getrs!('N', A, ipiv, b) end +@testset "inference in syev!/syevd!" begin + for T in (Float32, Float64), CT in (T, Complex{T}) + A = rand(CT, 4,4) + @inferred (A -> LAPACK.syev!('N', 'U', A))(A) + @inferred (A -> LAPACK.syev!('V', 'U', A))(A) + @inferred (A -> LAPACK.syevd!('N', 'U', A))(A) + @inferred (A -> LAPACK.syevd!('V', 'U', A))(A) + end +end + end # module TestLAPACK diff --git a/stdlib/LinearAlgebra/test/symmetric.jl b/stdlib/LinearAlgebra/test/symmetric.jl index b2fc9214f1ca4..23556c6548742 100644 --- a/stdlib/LinearAlgebra/test/symmetric.jl +++ b/stdlib/LinearAlgebra/test/symmetric.jl @@ -978,4 +978,15 @@ end @test conj(H) == conj(Array(H)) end +@testset "tr for block matrices" begin + m = [1 2; 3 4] + for b in (m, m * (1 + im)) + M = fill(b, 3, 3) + for ST in (Symmetric, Hermitian) + S = ST(M) + @test tr(S) == sum(diag(S)) + end + end +end + end # module TestSymmetric diff --git a/stdlib/LinearAlgebra/test/triangular.jl b/stdlib/LinearAlgebra/test/triangular.jl index d4dff286c7997..98334639c11e9 100644 --- a/stdlib/LinearAlgebra/test/triangular.jl +++ b/stdlib/LinearAlgebra/test/triangular.jl @@ -1056,4 +1056,18 @@ end @test V == Diagonal([1, 1]) end +@testset "preserve structure in scaling by NaN" begin + M = rand(Int8,2,2) + for (Ts, TD) in (((UpperTriangular, UnitUpperTriangular), UpperTriangular), + ((LowerTriangular, UnitLowerTriangular), LowerTriangular)) + for T in Ts + U = T(M) + for V in (U * NaN, NaN * U, U / NaN, NaN \ U) + @test V isa TD{Float64, Matrix{Float64}} + @test all(isnan, diag(V)) + end + end + end +end + end # module TestTriangular diff --git a/stdlib/LinearAlgebra/test/tridiag.jl b/stdlib/LinearAlgebra/test/tridiag.jl index 487f808076707..4f84988cabd50 100644 --- a/stdlib/LinearAlgebra/test/tridiag.jl +++ b/stdlib/LinearAlgebra/test/tridiag.jl @@ -468,7 +468,7 @@ end end @testset "SymTridiagonal/Tridiagonal block matrix" begin - M = [1 2; 2 4] + M = [1 2; 3 4] n = 5 A = SymTridiagonal(fill(M, n), fill(M, n-1)) @test @inferred A[1,1] == Symmetric(M) @@ -482,6 +482,9 @@ end @test_throws ArgumentError diag(A, n+1) @test_throws ArgumentError diag(A, -n-1) + @test tr(A) == sum(diag(A)) + @test issymmetric(tr(A)) + A = Tridiagonal(fill(M, n-1), fill(M, n), fill(M, n-1)) @test @inferred A[1,1] == M @test @inferred A[1,2] == M @@ -830,4 +833,22 @@ end @test axes(B) === (ax, ax) end +@testset "rmul!/lmul! with banded matrices" begin + dl, d, du = rand(3), rand(4), rand(3) + A = Tridiagonal(dl, d, du) + D = Diagonal(d) + @test rmul!(copy(A), D) ≈ A * D + @test lmul!(D, copy(A)) ≈ D * A + + @testset "non-commutative" begin + S32 = SizedArrays.SizedArray{(3,2)}(rand(3,2)) + S33 = SizedArrays.SizedArray{(3,3)}(rand(3,3)) + S22 = SizedArrays.SizedArray{(2,2)}(rand(2,2)) + T = Tridiagonal(fill(S32,3), fill(S32, 4), fill(S32, 3)) + D = Diagonal(fill(S22, size(T,2))) + @test rmul!(copy(T), D) ≈ T * D + D = Diagonal(fill(S33, size(T,1))) + @test lmul!(D, copy(T)) ≈ D * T + end +end end # module TestTridiagonal diff --git a/stdlib/Pkg.version b/stdlib/Pkg.version index 0ee27e8dfbc03..9c627070566a9 100644 --- a/stdlib/Pkg.version +++ b/stdlib/Pkg.version @@ -1,4 +1,4 @@ PKG_BRANCH = release-1.11 -PKG_SHA1 = 8457d3eff518f75bbff2d8b183e66e44ae5c47f1 +PKG_SHA1 = 2ff691035e9b2780cbd2fcb8dd30b640d85edde2 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/Profile/src/Profile.jl b/stdlib/Profile/src/Profile.jl index ee3b178eda423..98520b2267dd8 100644 --- a/stdlib/Profile/src/Profile.jl +++ b/stdlib/Profile/src/Profile.jl @@ -507,7 +507,7 @@ end # based on the package ecosystem function short_path(spath::Symbol, filenamecache::Dict{Symbol, String}) return get!(filenamecache, spath) do - path = string(spath) + path = Base.fixup_stdlib_path(string(spath)) if isabspath(path) if ispath(path) # try to replace the file-system prefix with a short "@Module" one, @@ -683,7 +683,7 @@ function add_fake_meta(data; threadid = 1, taskid = 0xf0f0f0f0) for i = 1:length(data) val = data[i] if iszero(val) - # (threadid, taskid, cpu_cycle_clock, thread_sleeping) + # META_OFFSET_THREADID, META_OFFSET_TASKID, META_OFFSET_CPUCYCLECLOCK, META_OFFSET_SLEEPSTATE push!(data_with_meta, threadid, taskid, cpu_clock_cycle+=1, false+1, 0, 0) else push!(data_with_meta, val) diff --git a/stdlib/Profile/src/heapsnapshot_reassemble.jl b/stdlib/Profile/src/heapsnapshot_reassemble.jl index 50da13e550d82..2413ae538b8ac 100644 --- a/stdlib/Profile/src/heapsnapshot_reassemble.jl +++ b/stdlib/Profile/src/heapsnapshot_reassemble.jl @@ -99,40 +99,42 @@ function assemble_snapshot(in_prefix, io::IO) orphans = Set{UInt}() # nodes that have no incoming edges # Parse nodes with empty edge counts that we need to fill later - nodes_file = open(string(in_prefix, ".nodes"), "r") - for i in 1:length(nodes) - node_type = read(nodes_file, Int8) - node_name_idx = read(nodes_file, UInt) - id = read(nodes_file, UInt) - self_size = read(nodes_file, Int) - @assert read(nodes_file, Int) == 0 # trace_node_id - @assert read(nodes_file, Int8) == 0 # detachedness - - nodes.type[i] = node_type - nodes.name_idx[i] = node_name_idx - nodes.id[i] = id - nodes.self_size[i] = self_size - nodes.edge_count[i] = 0 # edge_count - # populate the orphans set with node index - push!(orphans, i-1) + open(string(in_prefix, ".nodes"), "r") do nodes_file + for i in 1:length(nodes) + node_type = read(nodes_file, Int8) + node_name_idx = read(nodes_file, UInt) + id = read(nodes_file, UInt) + self_size = read(nodes_file, Int) + @assert read(nodes_file, Int) == 0 # trace_node_id + @assert read(nodes_file, Int8) == 0 # detachedness + + nodes.type[i] = node_type + nodes.name_idx[i] = node_name_idx + nodes.id[i] = id + nodes.self_size[i] = self_size + nodes.edge_count[i] = 0 # edge_count + # populate the orphans set with node index + push!(orphans, i-1) + end end # Parse the edges to fill in the edge counts for nodes and correct the to_node offsets - edges_file = open(string(in_prefix, ".edges"), "r") - for i in 1:length(nodes.edges) - edge_type = read(edges_file, Int8) - edge_name_or_index = read(edges_file, UInt) - from_node = read(edges_file, UInt) - to_node = read(edges_file, UInt) - - nodes.edges.type[i] = edge_type - nodes.edges.name_or_index[i] = edge_name_or_index - nodes.edges.to_pos[i] = to_node * k_node_number_of_fields # 7 fields per node, the streaming format doesn't multiply the offset by 7 - nodes.edge_count[from_node + 1] += UInt32(1) # C and JSON use 0-based indexing - push!(nodes.edge_idxs[from_node + 1], i) # Index into nodes.edges - # remove the node from the orphans if it has at least one incoming edge - if to_node in orphans - delete!(orphans, to_node) + open(string(in_prefix, ".edges"), "r") do edges_file + for i in 1:length(nodes.edges) + edge_type = read(edges_file, Int8) + edge_name_or_index = read(edges_file, UInt) + from_node = read(edges_file, UInt) + to_node = read(edges_file, UInt) + + nodes.edges.type[i] = edge_type + nodes.edges.name_or_index[i] = edge_name_or_index + nodes.edges.to_pos[i] = to_node * k_node_number_of_fields # 7 fields per node, the streaming format doesn't multiply the offset by 7 + nodes.edge_count[from_node + 1] += UInt32(1) # C and JSON use 0-based indexing + push!(nodes.edge_idxs[from_node + 1], i) # Index into nodes.edges + # remove the node from the orphans if it has at least one incoming edge + if to_node in orphans + delete!(orphans, to_node) + end end end diff --git a/stdlib/REPL/src/Pkg_beforeload.jl b/stdlib/REPL/src/Pkg_beforeload.jl index 7d80cfee3d54f..a73282dd6bdd3 100644 --- a/stdlib/REPL/src/Pkg_beforeload.jl +++ b/stdlib/REPL/src/Pkg_beforeload.jl @@ -62,7 +62,9 @@ end function projname(project_file::String) if isfile(project_file) name = try - p = Base.TOML.Parser() + # The `nothing` here means that this TOML parser does not return proper Dates.jl + # objects - but that's OK since we're just checking the name here. + p = Base.TOML.Parser{nothing}() Base.TOML.reinit!(p, read(project_file, String); filepath=project_file) proj = Base.TOML.parse(p) get(proj, "name", nothing) diff --git a/stdlib/REPL/src/REPL.jl b/stdlib/REPL/src/REPL.jl index a210c2fe2cf95..f47772f7b9ec0 100644 --- a/stdlib/REPL/src/REPL.jl +++ b/stdlib/REPL/src/REPL.jl @@ -100,6 +100,8 @@ export LineEditREPL, StreamREPL +public TerminalMenus + import Base: AbstractDisplay, display, diff --git a/stdlib/REPL/src/TerminalMenus/TerminalMenus.jl b/stdlib/REPL/src/TerminalMenus/TerminalMenus.jl index 87869e84d9838..34ea00a39f5e7 100644 --- a/stdlib/REPL/src/TerminalMenus/TerminalMenus.jl +++ b/stdlib/REPL/src/TerminalMenus/TerminalMenus.jl @@ -1,5 +1,12 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license +""" + REPL.TerminalMenus + +A module that contains code for displaying text mode interactive menus. +Key exported symbols include [`REPL.TerminalMenus.RadioMenu`](@ref) and +[`REPL.TerminalMenus.MultiSelectMenu`](@ref). +""" module TerminalMenus terminal = nothing # The user terminal @@ -25,6 +32,9 @@ export Pager, request +public Config, config, MultiSelectConfig +public pick, cancel, writeline, options, numoptions, selected, header, keypress + # TODO: remove in Julia 2.0 # While not exported, AbstractMenu documented these as an extension interface @deprecate printMenu printmenu diff --git a/stdlib/REPL/test/precompilation.jl b/stdlib/REPL/test/precompilation.jl index 228cbd212a2c1..7efcf0b5e8282 100644 --- a/stdlib/REPL/test/precompilation.jl +++ b/stdlib/REPL/test/precompilation.jl @@ -15,8 +15,11 @@ if !Sys.iswindows() @testset "No interactive startup compilation" begin f, _ = mktemp() - # start an interactive session - cmd = `$(Base.julia_cmd()[1]) --trace-compile=$f -q --startup-file=no -i` + # start an interactive session, ensuring `TERM` is unset since it can trigger + # different amounts of precompilation stemming from `base/terminfo.jl` depending + # on the value, making the test here unreliable + cmd = addenv(`$(Base.julia_cmd()[1]) --trace-compile=$f -q --startup-file=no -i`, + Dict("TERM" => "")) pts, ptm = open_fake_pty() p = run(cmd, pts, pts, pts; wait=false) Base.close_stdio(pts) diff --git a/stdlib/Random/src/Random.jl b/stdlib/Random/src/Random.jl index 432e32a4de691..9d723647cccf8 100644 --- a/stdlib/Random/src/Random.jl +++ b/stdlib/Random/src/Random.jl @@ -29,6 +29,8 @@ export rand!, randn!, randcycle, randcycle!, AbstractRNG, MersenneTwister, RandomDevice, TaskLocalRNG, Xoshiro +public seed!, default_rng, Sampler, SamplerType, SamplerTrivial, SamplerSimple + ## general definitions """ diff --git a/stdlib/StyledStrings.version b/stdlib/StyledStrings.version index 81a599f125406..83fbece4c8bc0 100644 --- a/stdlib/StyledStrings.version +++ b/stdlib/StyledStrings.version @@ -1,4 +1,4 @@ STYLEDSTRINGS_BRANCH = main -STYLEDSTRINGS_SHA1 = ac472083359dde956aed8c61d43b8158ac84d9ce +STYLEDSTRINGS_SHA1 = f6035eb97b516862b16e36cab2ecc6ea8adc3d7c STYLEDSTRINGS_GIT_URL := https://github.com/JuliaLang/StyledStrings.jl.git STYLEDSTRINGS_TAR_URL = https://api.github.com/repos/JuliaLang/StyledStrings.jl/tarball/$1 diff --git a/stdlib/TOML/src/TOML.jl b/stdlib/TOML/src/TOML.jl index 7414b5dc686f4..94d2808c0bc24 100644 --- a/stdlib/TOML/src/TOML.jl +++ b/stdlib/TOML/src/TOML.jl @@ -38,16 +38,10 @@ performance if a larger number of small files are parsed. """ const Parser = Internals.Parser -""" - DTParser() - -Constructor for a TOML `Parser` which returns date and time objects from Dates. -""" -function DTParser(args...; kwargs...) - parser = Parser(args...; kwargs...) - parser.Dates = Dates - return parser -end +# Dates-enabled constructors +Parser() = Parser{Dates}() +Parser(io::IO) = Parser{Dates}(io) +Parser(str::String; filepath=nothing) = Parser{Dates}(str; filepath) """ parsefile(f::AbstractString) @@ -59,7 +53,7 @@ Parse file `f` and return the resulting table (dictionary). Throw a See also [`TOML.tryparsefile`](@ref). """ parsefile(f::AbstractString) = - Internals.parse(DTParser(readstring(f); filepath=abspath(f))) + Internals.parse(Parser(readstring(f); filepath=abspath(f))) parsefile(p::Parser, f::AbstractString) = Internals.parse(Internals.reinit!(p, readstring(f); filepath=abspath(f))) @@ -73,7 +67,7 @@ Parse file `f` and return the resulting table (dictionary). Return a See also [`TOML.parsefile`](@ref). """ tryparsefile(f::AbstractString) = - Internals.tryparse(DTParser(readstring(f); filepath=abspath(f))) + Internals.tryparse(Parser(readstring(f); filepath=abspath(f))) tryparsefile(p::Parser, f::AbstractString) = Internals.tryparse(Internals.reinit!(p, readstring(f); filepath=abspath(f))) @@ -87,7 +81,7 @@ Throw a [`ParserError`](@ref) upon failure. See also [`TOML.tryparse`](@ref). """ parse(str::AbstractString) = - Internals.parse(DTParser(String(str))) + Internals.parse(Parser(String(str))) parse(p::Parser, str::AbstractString) = Internals.parse(Internals.reinit!(p, String(str))) parse(io::IO) = parse(read(io, String)) @@ -103,7 +97,7 @@ Return a [`ParserError`](@ref) upon failure. See also [`TOML.parse`](@ref). """ tryparse(str::AbstractString) = - Internals.tryparse(DTParser(String(str))) + Internals.tryparse(Parser(String(str))) tryparse(p::Parser, str::AbstractString) = Internals.tryparse(Internals.reinit!(p, String(str))) tryparse(io::IO) = tryparse(read(io, String)) diff --git a/stdlib/TOML/test/values.jl b/stdlib/TOML/test/values.jl index be2ed3acce5b5..4fc49d47fc98d 100644 --- a/stdlib/TOML/test/values.jl +++ b/stdlib/TOML/test/values.jl @@ -4,16 +4,31 @@ using Test using TOML using TOML: Internals +# Construct an explicit Parser to test the "cached" version of parsing +const test_parser = TOML.Parser() + function testval(s, v) f = "foo = $s" + # First, test with the standard entrypoint parsed = TOML.parse(f)["foo"] return isequal(v, parsed) && typeof(v) == typeof(parsed) + (!isequal(v, parsed) || typeof(v) != typeof(parsed)) && return false + # Next, test with the "cached" (explicit Parser) entrypoint + parsed = TOML.parse(test_parser, f)["foo"] + (!isequal(v, parsed) || typeof(v) != typeof(parsed)) && return false + return true end function failval(s, v) f = "foo = $s" + # First, test with the standard entrypoint err = TOML.tryparse(f); return err isa TOML.Internals.ParserError && err.type == v + (!isa(err, TOML.Internals.ParserError) || err.type != v) && return false + # Next, test with the "cached" (explicit Parser) entrypoint + err = TOML.tryparse(test_parser, f); + (!isa(err, TOML.Internals.ParserError) || err.type != v) && return false + return true end @testset "Numbers" begin diff --git a/stdlib/libblastrampoline_jll/Project.toml b/stdlib/libblastrampoline_jll/Project.toml index 97ee9bb32eeb1..fef963a88f5fd 100644 --- a/stdlib/libblastrampoline_jll/Project.toml +++ b/stdlib/libblastrampoline_jll/Project.toml @@ -1,6 +1,6 @@ name = "libblastrampoline_jll" uuid = "8e850b90-86db-534c-a0d3-1478176c7d93" -version = "5.10.1+0" +version = "5.11.0+0" [deps] Libdl = "8f399da3-3557-5675-b5ff-fb832c97cbdb" diff --git a/test/abstractarray.jl b/test/abstractarray.jl index 0d5ad2f7282df..88cffbcf2947d 100644 --- a/test/abstractarray.jl +++ b/test/abstractarray.jl @@ -1423,6 +1423,15 @@ using .Main.OffsetArrays end end +@testset "Check push!($a, $args...)" for + a in (["foo", "Bar"], SimpleArray(["foo", "Bar"]), OffsetVector(["foo", "Bar"], 0:1)), + args in (("eenie",), ("eenie", "minie"), ("eenie", "minie", "mo")) + orig = copy(a) + push!(a, args...) + @test length(a) == length(orig) + length(args) + @test all(a[end-length(args)+1:end] .== args) +end + @testset "splatting into hvcat" begin t = (1, 2) @test [t...; 3 4] == [1 2; 3 4] @@ -1771,6 +1780,9 @@ end @test_throws ArgumentError stack([1:3, 4:6]; dims=3) @test_throws ArgumentError stack(abs2, 1:3; dims=2) + @test stack(["hello", "world"]) isa Matrix{Char} + @test_throws DimensionMismatch stack(["hello", "world!"]) # had a bug in error printing + # Empty @test_throws ArgumentError stack(()) @test_throws ArgumentError stack([]) diff --git a/test/ambiguous.jl b/test/ambiguous.jl index ac5b524931046..6a5a66a36e0e5 100644 --- a/test/ambiguous.jl +++ b/test/ambiguous.jl @@ -451,4 +451,20 @@ cc46601(::Type{T}, x::Int) where {T<:AbstractString} = 7 @test length(methods(cc46601, Tuple{Type{<:Integer}, Integer})) == 2 @test length(Base.methods_including_ambiguous(cc46601, Tuple{Type{<:Integer}, Integer})) == 7 +# Issue #55231 +struct U55231{P} end +struct V55231{P} end +U55231(::V55231) = nothing +(::Type{T})(::V55231) where {T<:U55231} = nothing +@test length(methods(U55231)) == 2 +U55231(a, b) = nothing +@test length(methods(U55231)) == 3 +struct S55231{P} end +struct T55231{P} end +(::Type{T})(::T55231) where {T<:S55231} = nothing +S55231(::T55231) = nothing +@test length(methods(S55231)) == 2 +S55231(a, b) = nothing +@test length(methods(S55231)) == 3 + nothing diff --git a/test/atomics.jl b/test/atomics.jl index 0d6c841073ad5..b2fc0e13c948d 100644 --- a/test/atomics.jl +++ b/test/atomics.jl @@ -128,6 +128,7 @@ test_field_operators(ARefxy{Any}(123_10, 123_20)) test_field_operators(ARefxy{Union{Nothing,Int}}(123_10, nothing)) test_field_operators(ARefxy{Complex{Int32}}(123_10, 123_20)) test_field_operators(ARefxy{Complex{Int128}}(123_10, 123_20)) +test_field_operators(ARefxy{Complex{Real}}(123_10, 123_20)) test_field_operators(ARefxy{PadIntA}(123_10, 123_20)) test_field_operators(ARefxy{PadIntB}(123_10, 123_20)) #FIXME: test_field_operators(ARefxy{Int24}(123_10, 123_20)) @@ -316,6 +317,7 @@ test_field_orderings(ARefxy{Any}(true, false), true, false) test_field_orderings(ARefxy{Union{Nothing,Missing}}(nothing, missing), nothing, missing) test_field_orderings(ARefxy{Union{Nothing,Int}}(nothing, 123_1), nothing, 123_1) test_field_orderings(Complex{Int128}(10, 30), Complex{Int128}(20, 40)) +test_field_orderings(Complex{Real}(10, 30), Complex{Real}(20, 40)) test_field_orderings(10.0, 20.0) test_field_orderings(NaN, Inf) @@ -481,6 +483,7 @@ test_global_operators(Any) test_global_operators(Union{Nothing,Int}) test_global_operators(Complex{Int32}) test_global_operators(Complex{Int128}) +test_global_operators(Complex{Real}) test_global_operators(PadIntA) test_global_operators(PadIntB) #FIXME: test_global_operators(Int24) @@ -604,6 +607,7 @@ test_global_orderings(Any, true, false) test_global_orderings(Union{Nothing,Missing}, nothing, missing) test_global_orderings(Union{Nothing,Int}, nothing, 123_1) test_global_orderings(Complex{Int128}, Complex{Int128}(10, 30), Complex{Int128}(20, 40)) +test_global_orderings(Complex{Real}, Complex{Real}(10, 30), Complex{Real}(20, 40)) test_global_orderings(Float64, 10.0, 20.0) test_global_orderings(Float64, NaN, Inf) @@ -757,6 +761,7 @@ test_memory_operators(Any) test_memory_operators(Union{Nothing,Int}) test_memory_operators(Complex{Int32}) test_memory_operators(Complex{Int128}) +test_memory_operators(Complex{Real}) test_memory_operators(PadIntA) test_memory_operators(PadIntB) #FIXME: test_memory_operators(Int24) @@ -944,6 +949,7 @@ test_memory_orderings(Any, true, false) test_memory_orderings(Union{Nothing,Missing}, nothing, missing) test_memory_orderings(Union{Nothing,Int}, nothing, 123_1) test_memory_orderings(Complex{Int128}(10, 30), Complex{Int128}(20, 40)) +test_memory_orderings(Complex{Real}(10, 30), Complex{Real}(20, 40)) test_memory_orderings(10.0, 20.0) test_memory_orderings(NaN, Inf) diff --git a/test/ccall.jl b/test/ccall.jl index 8b1fefdfc66e4..8fe3c8cbefe03 100644 --- a/test/ccall.jl +++ b/test/ccall.jl @@ -1937,3 +1937,8 @@ end # issue #52025 @test Base.unsafe_convert(Ptr{Ptr{Cchar}}, Base.cconvert(Ptr{Ptr{Cchar}}, map(pointer, ["ab"]))) isa Ptr{Ptr{Cchar}} + +#issue #54725 +for A in (reinterpret(UInt, [0]), reshape([0, 0], 1, 2)) + @test pointer(A) == Base.unsafe_convert(Ptr{Cvoid}, A) == Base.unsafe_convert(Ptr{Int}, A) +end diff --git a/test/compiler/codegen.jl b/test/compiler/codegen.jl index e8f43236c1bd1..ecba1e3e23e08 100644 --- a/test/compiler/codegen.jl +++ b/test/compiler/codegen.jl @@ -903,3 +903,16 @@ function foonopreds() pkgid.uuid !== nothing ? pkgid.uuid : false end @test foonopreds() !== nothing + +# issue 55396 +struct Incomplete55396 + x::Tuple{Int} + y::Int + @noinline Incomplete55396(x::Int) = new((x,)) +end +let x = Incomplete55396(55396) + @test x.x === (55396,) +end + +# Core.getptls() special handling +@test !occursin("call ptr @jlplt", get_llvm(Core.getptls, Tuple{})) #It should lower to a direct load of the ptls and not a ccall diff --git a/test/complex.jl b/test/complex.jl index d798cfe16489c..63304652ee7d8 100644 --- a/test/complex.jl +++ b/test/complex.jl @@ -1215,3 +1215,9 @@ end @test !iseven(7+0im) && isodd(7+0im) @test !iseven(6+1im) && !isodd(7+1im) end + +@testset "issue #55266" begin + for T in (Float16, Float32, Float64) + @test isapprox(atanh(1+im*floatmin(T)), Complex{T}(atanh(1+im*big(floatmin(T))))) + end +end diff --git a/test/core.jl b/test/core.jl index 9efe3063c1797..9750cc519a746 100644 --- a/test/core.jl +++ b/test/core.jl @@ -7477,6 +7477,13 @@ struct A43411{S, T} end @test isbitstype(A43411{(:a,), Tuple{Int}}) +# issue #55189 +struct A55189{N} + children::NTuple{N,A55189{N}} +end +@test fieldtype(A55189{2}, 1) === Tuple{A55189{2}, A55189{2}} +@assert !isbitstype(A55189{2}) + # issue #44614 struct T44614_1{T} m::T diff --git a/test/llvmpasses/alloc-opt-bits.ll b/test/llvmpasses/alloc-opt-bits.ll new file mode 100644 index 0000000000000..e19093f46f815 --- /dev/null +++ b/test/llvmpasses/alloc-opt-bits.ll @@ -0,0 +1,37 @@ +; This file is a part of Julia. License is MIT: https://julialang.org/license + +; RUN: opt --load-pass-plugin=libjulia-codegen%shlibext -passes='function(AllocOpt)' -S %s | FileCheck %s + + +@tag = external addrspace(10) global {} + +@glob = external addrspace(10) global {} + +; Test that the gc_preserve intrinsics are deleted directly. + +; CHECK-LABEL: @ptr_and_bits +; CHECK-NOT: alloca +; CHECK: call noalias ptr addrspace(10) @julia.gc_alloc_obj + +define void @ptr_and_bits(ptr %fptr, i1 %b, i1 %b2, i32 %idx) { + %pgcstack = call ptr @julia.get_pgcstack() + %ptls = call ptr @julia.ptls_states() + %ptls_i8 = bitcast ptr %ptls to ptr + %v = call noalias ptr addrspace(10) @julia.gc_alloc_obj(ptr %ptls_i8, i64 16, ptr addrspace(10) @tag) + + %g0 = getelementptr { i64, ptr addrspace(10) }, ptr addrspace(10) %v, i32 %idx, i32 1 + store ptr addrspace(10) @glob, ptr addrspace(10) %g0 + + %g1 = getelementptr { i64, ptr addrspace(10) }, ptr addrspace(10) %v, i32 %idx, i32 0 + store i64 7, ptr addrspace(10) %g1 + + %res = load ptr addrspace(10), ptr addrspace(10) %g0 + %res2 = load i64, ptr addrspace(10) %g1 + ret void +} + +declare noalias ptr addrspace(10) @julia.gc_alloc_obj(ptr, i64, ptr addrspace(10)) + +declare ptr @julia.ptls_states() + +declare ptr @julia.get_pgcstack() diff --git a/test/llvmpasses/parsing.ll b/test/llvmpasses/parsing.ll index 6a5909ff5fd40..e0a726176b225 100644 --- a/test/llvmpasses/parsing.ll +++ b/test/llvmpasses/parsing.ll @@ -1,6 +1,9 @@ ; COM: NewPM-only test, tests for ability to parse Julia passes -; RUN: opt --opaque-pointers=0 --load-pass-plugin=libjulia-codegen%shlibext -passes='module(CPUFeatures,RemoveNI,JuliaMultiVersioning,RemoveJuliaAddrspaces,LowerPTLSPass,function(DemoteFloat16,CombineMulAdd,LateLowerGCFrame,FinalLowerGC,AllocOpt,PropagateJuliaAddrspaces,LowerExcHandlers,GCInvariantVerifier,loop(LowerSIMDLoop,JuliaLICM),GCInvariantVerifier,GCInvariantVerifier),LowerPTLSPass,LowerPTLSPass,JuliaMultiVersioning,JuliaMultiVersioning)' -S %s -o /dev/null +; RUN: opt --load-pass-plugin=libjulia-codegen%shlibext -passes='module(CPUFeatures,RemoveNI,JuliaMultiVersioning,RemoveJuliaAddrspaces,LowerPTLSPass,function(DemoteFloat16,CombineMulAdd,LateLowerGCFrame,FinalLowerGC,AllocOpt,PropagateJuliaAddrspaces,LowerExcHandlers,GCInvariantVerifier,loop(LowerSIMDLoop,JuliaLICM),GCInvariantVerifier,GCInvariantVerifier),LowerPTLSPass,LowerPTLSPass,JuliaMultiVersioning,JuliaMultiVersioning)' -S %s -o /dev/null +; RUN: opt --load-pass-plugin=libjulia-codegen%shlibext -passes="julia" -S %s -o /dev/null +; RUN: opt --load-pass-plugin=libjulia-codegen%shlibext -passes="julia" -S %s -o /dev/null +; RUN: opt --load-pass-plugin=libjulia-codegen%shlibext -passes="julia" -S %s -o /dev/null define void @test() { ret void diff --git a/test/offsetarray.jl b/test/offsetarray.jl index 9d6a8b08c0b1f..c50f38c382385 100644 --- a/test/offsetarray.jl +++ b/test/offsetarray.jl @@ -865,6 +865,15 @@ end @test CartesianIndices(A) == CartesianIndices(B) end +@testset "overflowing show" begin + A = OffsetArray(repeat([1], 1), typemax(Int)-1) + b = IOBuffer(maxsize=10) + show(b, A) + @test String(take!(b)) == "[1]" + show(b, (A, A)) + @test String(take!(b)) == "([1], [1])" +end + @testset "indexing views (#53249)" begin v = view([1,2,3,4], :) @test v[Base.IdentityUnitRange(2:3)] == OffsetArray(2:3, 2:3) diff --git a/test/precompile.jl b/test/precompile.jl index be894e5d26133..65b94e81c31ab 100644 --- a/test/precompile.jl +++ b/test/precompile.jl @@ -619,6 +619,10 @@ precompile_test_harness(false) do dir @test Base.invokelatest(Baz.baz) === 1 @test Baz === UseBaz.Baz + # should not throw if the cachefile does not exist + @test !isfile("DoesNotExist.ji") + @test Base.stale_cachefile("", "DoesNotExist.ji") === true + # Issue #12720 FooBar1_file = joinpath(dir, "FooBar1.jl") write(FooBar1_file, diff --git a/test/reducedim.jl b/test/reducedim.jl index 77ed94664539f..18577f7b427ba 100644 --- a/test/reducedim.jl +++ b/test/reducedim.jl @@ -587,6 +587,30 @@ end @test B[argmin(B, dims=[2, 3])] == @inferred(minimum(B, dims=[2, 3])) end +@testset "careful with @inbounds" begin + Base.@propagate_inbounds f(x) = x == 2 ? x[-10000] : x + Base.@propagate_inbounds op(x,y) = x[-10000] + y[-10000] + for (arr, dims) in (([1,1,2], 1), ([1 1 2], 2), ([ones(Int,256);2], 1)) + @test_throws BoundsError mapreduce(f, +, arr) + @test_throws BoundsError mapreduce(f, +, arr; dims) + @test_throws BoundsError mapreduce(f, +, arr; dims, init=0) + @test_throws BoundsError mapreduce(identity, op, arr) + try + #=@test_throws BoundsError=# mapreduce(identity, op, arr; dims) + catch ex + @test_broken ex isa BoundsError + end + @test_throws BoundsError mapreduce(identity, op, arr; dims, init=0) + + @test_throws BoundsError findmin(f, arr) + @test_throws BoundsError findmin(f, arr; dims) + + @test_throws BoundsError mapreduce(f, max, arr) + @test_throws BoundsError mapreduce(f, max, arr; dims) + @test_throws BoundsError mapreduce(f, max, arr; dims, init=0) + end +end + @testset "in-place reductions with mismatched dimensionalities" begin B = reshape(1:24, 4, 3, 2) for R in (fill(0, 4), fill(0, 4, 1), fill(0, 4, 1, 1)) diff --git a/test/specificity.jl b/test/specificity.jl index 9b605444bad42..6264b59df5f75 100644 --- a/test/specificity.jl +++ b/test/specificity.jl @@ -316,3 +316,14 @@ end @test args_morespecific(Tuple{typeof(Union{}), Any}, Tuple{Any, Type{Union{}}}) @test args_morespecific(Tuple{Type{Union{}}, Type{Union{}}, Any}, Tuple{Type{Union{}}, Any, Type{Union{}}}) @test args_morespecific(Tuple{Type{Union{}}, Type{Union{}}, Any, Type{Union{}}}, Tuple{Type{Union{}}, Any, Type{Union{}}, Type{Union{}}}) + +# requires assertions enabled +let root = NTuple + N = root.var + T = root.body.var + x1 = root.body.body + x2 = Dict{T,Tuple{N}} + A = UnionAll(N, UnionAll(T, Tuple{Union{x1, x2}})) + B = Tuple{Union{UnionAll(N, UnionAll(T, x1)), UnionAll(N, UnionAll(T, x2))}} + @ccall jl_type_morespecific_no_subtype(A::Any, B::Any)::Cint +end diff --git a/test/strings/annotated.jl b/test/strings/annotated.jl index 944608fa8c4a9..62bf9a0c91c00 100644 --- a/test/strings/annotated.jl +++ b/test/strings/annotated.jl @@ -103,6 +103,8 @@ end [(1:4, :label => 5), (5:5, :label => 2), (6:9, :label => 5)]) + @test join((String(str1), str1), ' ') == + Base.AnnotatedString("test test", [(6:9, :label => 5)]) @test repeat(str1, 2) == Base.AnnotatedString("testtest", [(1:8, :label => 5)]) @test repeat(str2, 2) == Base.AnnotatedString("casecase", [(2:3, :label => "oomph"), (6:7, :label => "oomph")]) diff --git a/test/strings/io.jl b/test/strings/io.jl index f1fe0c24e8aea..3387ebc1e6794 100644 --- a/test/strings/io.jl +++ b/test/strings/io.jl @@ -339,3 +339,8 @@ end @testset "`string` return types" begin @test all(T -> T <: AbstractString, Base.return_types(string)) end + +@testset "type stable `join` (#55389)" begin + itr = ("foo" for _ in 1:100) + @test Base.return_types(join, (typeof(itr),))[] == String +end diff --git a/test/subtype.jl b/test/subtype.jl index c26f4fc9d30e2..7be869107b432 100644 --- a/test/subtype.jl +++ b/test/subtype.jl @@ -707,16 +707,17 @@ macro testintersect(a, b, result) a = esc(a) b = esc(b) result = esc(result) - Base.remove_linenums!(quote + # use a manual macrocall expression since Test will examine this __source__ value + return quote # test real intersect - @test $cmp(_type_intersect($a, $b), $result) - @test $cmp(_type_intersect($b, $a), $result) + $(Expr(:macrocall, :var"@test", __source__, :($cmp(_type_intersect($a, $b), $result)))) + $(Expr(:macrocall, :var"@test", __source__, :($cmp(_type_intersect($b, $a), $result)))) # test simplified intersect if !($result === Union{}) - @test typeintersect($a, $b) != Union{} - @test typeintersect($b, $a) != Union{} + $(Expr(:macrocall, :var"@test", __source__, :(typeintersect($a, $b) != Union{}))) + $(Expr(:macrocall, :var"@test", __source__, :(typeintersect($b, $a) != Union{}))) end - end) + end end abstract type IT4805_2{N, T} end @@ -2267,31 +2268,46 @@ let S = Tuple{Integer, U} where {II<:Array, U<:Tuple{Vararg{II, 1}}} @testintersect(S, Tuple{Int, U} where {N, U<:Tuple{Any,Any,Vararg{Any,N}}}, Union{}) end +function equal_envs(env1, env2) + length(env1) == length(env2) || return false + for i = 1:length(env1) + a = env1[i] + b = env2[i] + if a isa TypeVar + if !(b isa TypeVar && a.name == b.name && a.lb == b.lb && a.ub == b.ub) + return false + end + elseif !(a == b) + return false + end + end + return true +end + # issue #43064 let - env_tuple(@nospecialize(x), @nospecialize(y)) = (intersection_env(x, y)[2]...,) - all_var(x::UnionAll) = (x.var, all_var(x.body)...) - all_var(x::DataType) = () + env_tuple(@nospecialize(x), @nospecialize(y)) = intersection_env(x, y)[2] TT0 = Tuple{Type{T},Union{Real,Missing,Nothing}} where {T} TT1 = Union{Type{Int8},Type{Int16}} @test env_tuple(Tuple{TT1,Missing}, TT0) === env_tuple(Tuple{TT1,Nothing}, TT0) === - env_tuple(Tuple{TT1,Int}, TT0) === all_var(TT0) + env_tuple(Tuple{TT1,Int}, TT0) === + Core.svec(TT0.var) TT0 = Tuple{T1,T2,Union{Real,Missing,Nothing}} where {T1,T2} TT1 = Tuple{T1,T2,Union{Real,Missing,Nothing}} where {T2,T1} TT2 = Tuple{Union{Int,Int8},Union{Int,Int8},Int} TT3 = Tuple{Int,Union{Int,Int8},Int} - @test env_tuple(TT2, TT0) === all_var(TT0) - @test env_tuple(TT2, TT1) === all_var(TT1) - @test env_tuple(TT3, TT0) === Base.setindex(all_var(TT0), Int, 1) - @test env_tuple(TT3, TT1) === Base.setindex(all_var(TT1), Int, 2) + @test equal_envs(env_tuple(TT2, TT0), Core.svec(TypeVar(:T1, Union{Int, Int8}), TypeVar(:T2, Union{Int, Int8}))) + @test equal_envs(env_tuple(TT2, TT1), Core.svec(TypeVar(:T2, Union{Int, Int8}), TypeVar(:T1, Union{Int, Int8}))) + @test equal_envs(env_tuple(TT3, TT0), Core.svec(Int, TypeVar(:T2, Union{Int, Int8}))) + @test equal_envs(env_tuple(TT3, TT1), Core.svec(TypeVar(:T2, Union{Int, Int8}), Int)) TT0 = Tuple{T1,T2,T1,Union{Real,Missing,Nothing}} where {T1,T2} TT1 = Tuple{T1,T2,T1,Union{Real,Missing,Nothing}} where {T2,T1} TT2 = Tuple{Int,Union{Int,Int8},Int,Int} - @test env_tuple(TT2, TT0) === Base.setindex(all_var(TT0), Int, 1) - @test env_tuple(TT2, TT1) === Base.setindex(all_var(TT1), Int, 2) + @test equal_envs(env_tuple(TT2, TT0), Core.svec(Int, TypeVar(:T2, Union{Int, Int8}))) + @test equal_envs(env_tuple(TT2, TT1), Core.svec(TypeVar(:T2, Union{Int, Int8}), Int)) end #issue #46735 @@ -2380,12 +2396,41 @@ let S = Tuple{T2, V2} where {T2, N2, V2<:(Array{S2, N2} where {S2 <: T2})}, @testintersect(S, T, !Union{}) end -# A simple case which has a small local union. -# make sure the env is not widened too much when we intersect(Int8, Int8). -struct T48006{A1,A2,A3} end -@testintersect(Tuple{T48006{Float64, Int, S1}, Int} where {F1<:Real, S1<:Union{Int8, Val{F1}}}, - Tuple{T48006{F2, I, S2}, I} where {F2<:Real, I<:Int, S2<:Union{Int8, Val{F2}}}, - Tuple{T48006{Float64, Int, S1}, Int} where S1<:Union{Val{Float64}, Int8}) +let S = Dict{Int, S1} where {F1, S1<:Union{Int8, Val{F1}}}, + T = Dict{F2, S2} where {F2, S2<:Union{Int8, Val{F2}}} + @test_broken typeintersect(S, T) == Dict{Int, S} where S<:Union{Val{Int}, Int8} + @test typeintersect(T, S) == Dict{Int, S} where S<:Union{Val{Int}, Int8} +end + +# Ensure inner `intersect_all` never under-esitimate. +let S = Tuple{F1, Dict{Int, S1}} where {F1, S1<:Union{Int8, Val{F1}}}, + T = Tuple{Any, Dict{F2, S2}} where {F2, S2<:Union{Int8, Val{F2}}} + @test Tuple{Nothing, Dict{Int, Int8}} <: S + @test Tuple{Nothing, Dict{Int, Int8}} <: T + @test Tuple{Nothing, Dict{Int, Int8}} <: typeintersect(S, T) + @test Tuple{Nothing, Dict{Int, Int8}} <: typeintersect(T, S) +end + +let S = Tuple{F1, Val{S1}} where {F1, S1<:Dict{F1}} + T = Tuple{Any, Val{S2}} where {F2, S2<:Union{map(T->Dict{T}, Base.BitInteger_types)...}} + ST = typeintersect(S, T) + TS = typeintersect(S, T) + for U in Base.BitInteger_types + @test Tuple{U, Val{Dict{U,Nothing}}} <: S + @test Tuple{U, Val{Dict{U,Nothing}}} <: T + @test Tuple{U, Val{Dict{U,Nothing}}} <: ST + @test Tuple{U, Val{Dict{U,Nothing}}} <: TS + end +end + +#issue 55206 +struct T55206{A,B<:Complex{A},C<:Union{Dict{Nothing},Dict{A}}} end +@testintersect(T55206, T55206{<:Any,<:Any,<:Dict{Nothing}}, T55206{A,<:Complex{A},<:Dict{Nothing}} where {A}) +@testintersect( + Tuple{Dict{Int8, Int16}, Val{S1}} where {F1, S1<:AbstractSet{F1}}, + Tuple{Dict{T1, T2}, Val{S2}} where {T1, T2, S2<:Union{Set{T1},Set{T2}}}, + Tuple{Dict{Int8, Int16}, Val{S1}} where {S1<:Union{Set{Int8},Set{Int16}}} +) f48167(::Type{Val{L2}}, ::Type{Union{Val{L1}, Set{R}}}) where {L1, R, L2<:L1} = 1 f48167(::Type{Val{L1}}, ::Type{Union{Val{L2}, Set{R}}}) where {L1, R, L2<:L1} = 2 @@ -2554,7 +2599,7 @@ end let T = Tuple{Union{Type{T}, Type{S}}, Union{Val{T}, Val{S}}, Union{Val{T}, S}} where T<:Val{A} where A where S<:Val, S = Tuple{Type{T}, T, Val{T}} where T<:(Val{S} where S<:Val) # optimal = Union{}? - @test typeintersect(T, S) == Tuple{Type{A}, Union{Val{A}, Val{S} where S<:Union{Val, A}, Val{x} where x<:Val, Val{x} where x<:Union{Val, A}}, Val{A}} where A<:(Val{S} where S<:Val) + @test typeintersect(T, S) == Tuple{Type{T}, Union{Val{T}, Val{S}}, Val{T}} where {S<:Val, T<:Val} @test typeintersect(S, T) == Tuple{Type{T}, Union{Val{T}, Val{S}}, Val{T}} where {T<:Val, S<:(Union{Val{A}, Val} where A)} end @@ -2657,3 +2702,22 @@ let S = Tuple{Val{<:T}, Union{Int,T}} where {T}, @testintersect(S, T, !Union{}) @test !Base.has_free_typevars(typeintersect(S, T)) end + +#issue 55230 +let T1 = NTuple{12, Union{Val{1}, Val{2}, Val{3}, Val{4}, Val{5}, Val{6}}} + T2 = Tuple{<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any} + @test T1 <: T2 + T2 = Tuple{<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Val} + @test T1 <: T2 + T2 = Tuple{<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Real} + @test !(T1 <: T2) + T2 = Tuple{<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Union{Val,Real}} + @test T1 <: T2 + T2 = Tuple{<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Union{String,Real}} + @test !(T1 <: T2) + T2 = Tuple{<:Union{Val,Real},<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any} + @test T1 <: T2 + T2 = Tuple{<:Union{String,Real},<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any} + @test !(T1 <: T2) + @test Tuple{Union{Val{1},Val{2}}} <: Tuple{S} where {T, S<:Val{T}} +end diff --git a/test/testhelpers/SizedArrays.jl b/test/testhelpers/SizedArrays.jl index 43bc27e630479..bc02fb5cbbd20 100644 --- a/test/testhelpers/SizedArrays.jl +++ b/test/testhelpers/SizedArrays.jl @@ -23,6 +23,9 @@ Base.first(::SOneTo) = 1 Base.last(r::SOneTo) = length(r) Base.show(io::IO, r::SOneTo) = print(io, "SOneTo(", length(r), ")") +Broadcast.axistype(a::Base.OneTo, s::SOneTo) = s +Broadcast.axistype(s::SOneTo, a::Base.OneTo) = s + struct SizedArray{SZ,T,N,A<:AbstractArray} <: AbstractArray{T,N} data::A function SizedArray{SZ}(data::AbstractArray{T,N}) where {SZ,T,N} @@ -43,10 +46,25 @@ Base.size(a::SizedArray) = size(typeof(a)) Base.size(::Type{<:SizedArray{SZ}}) where {SZ} = SZ Base.axes(a::SizedArray) = map(SOneTo, size(a)) Base.getindex(A::SizedArray, i...) = getindex(A.data, i...) +Base.setindex!(A::SizedArray, v, i...) = setindex!(A.data, v, i...) Base.zero(::Type{T}) where T <: SizedArray = SizedArray{size(T)}(zeros(eltype(T), size(T))) +Base.parent(S::SizedArray) = S.data +(S1::SizedArray{SZ}, S2::SizedArray{SZ}) where {SZ} = SizedArray{SZ}(S1.data + S2.data) ==(S1::SizedArray{SZ}, S2::SizedArray{SZ}) where {SZ} = S1.data == S2.data +homogenize_shape(t::Tuple) = (_homogenize_shape(first(t)), homogenize_shape(Base.tail(t))...) +homogenize_shape(::Tuple{}) = () +_homogenize_shape(x::Integer) = x +_homogenize_shape(x::AbstractUnitRange) = length(x) +const Dims = Union{Integer, Base.OneTo, SOneTo} +function Base.similar(::Type{A}, shape::Tuple{Dims, Vararg{Dims}}) where {A<:AbstractArray} + similar(A, homogenize_shape(shape)) +end +function Base.similar(::Type{A}, shape::Tuple{SOneTo, Vararg{SOneTo}}) where {A<:AbstractArray} + R = similar(A, length.(shape)) + SizedArray{length.(shape)}(R) +end + const SizedMatrixLike = Union{SizedMatrix, Transpose{<:Any, <:SizedMatrix}, Adjoint{<:Any, <:SizedMatrix}} _data(S::SizedArray) = S.data diff --git a/test/threads.jl b/test/threads.jl index ab35c327196ef..d3b2dee323440 100644 --- a/test/threads.jl +++ b/test/threads.jl @@ -344,12 +344,12 @@ end @testset "jl_*affinity" begin cpumasksize = @ccall uv_cpumask_size()::Cint - if !Sys.iswindows() && cpumasksize > 0 # otherwise affinities are not supported on the platform (UV_ENOTSUP) - mask = zeros(Cchar, cpumasksize); + if cpumasksize > 0 # otherwise affinities are not supported on the platform (UV_ENOTSUP) jl_getaffinity = (tid, mask, cpumasksize) -> ccall(:jl_getaffinity, Int32, (Int16, Ptr{Cchar}, Int32), tid, mask, cpumasksize) jl_setaffinity = (tid, mask, cpumasksize) -> ccall(:jl_setaffinity, Int32, (Int16, Ptr{Cchar}, Int32), tid, mask, cpumasksize) - @test jl_getaffinity(1, mask, cpumasksize) == 0 - fill!(mask, 1) - @test jl_setaffinity(1, mask, cpumasksize) == 0 + mask = zeros(Cchar, cpumasksize) + @test jl_getaffinity(0, mask, cpumasksize) == 0 + @test !all(iszero, mask) + @test jl_setaffinity(0, mask, cpumasksize) == 0 end end