From 5e08c20c7094b032d68cd4f200fb4f707a292d2e Mon Sep 17 00:00:00 2001 From: "Steven G. Johnson" Date: Tue, 10 May 2022 19:28:19 -0400 Subject: [PATCH 01/38] add bswap(x::Bool)=x (#45265) * missing bswap for Bool * add a test --- base/int.jl | 2 +- test/int.jl | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/base/int.jl b/base/int.jl index 41e53e990be5b..8f60312551086 100644 --- a/base/int.jl +++ b/base/int.jl @@ -387,7 +387,7 @@ julia> string(bswap(1), base = 2) "100000000000000000000000000000000000000000000000000000000" ``` """ -bswap(x::Union{Int8, UInt8}) = x +bswap(x::Union{Int8, UInt8, Bool}) = x bswap(x::Union{Int16, UInt16, Int32, UInt32, Int64, UInt64, Int128, UInt128}) = bswap_int(x) diff --git a/test/int.jl b/test/int.jl index d7b79fb6c1e0c..8b77a59e0c5e2 100644 --- a/test/int.jl +++ b/test/int.jl @@ -70,6 +70,7 @@ end @test unsigned(Bool) === typeof(unsigned(true)) end @testset "bswap" begin + @test bswap(true) == true @test bswap(Int8(3)) == 3 @test bswap(UInt8(3)) === 0x3 @test bswap(Int16(3)) == 256*3 From 94ddc170eeeabac64dde9377be67975b5e39d531 Mon Sep 17 00:00:00 2001 From: trathi05 <69152703+trathi05@users.noreply.github.com> Date: Tue, 10 May 2022 22:16:52 -0500 Subject: [PATCH 02/38] Improve docstring for `Core.Symbol` (#45267) --- base/docs/basedocs.jl | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/base/docs/basedocs.jl b/base/docs/basedocs.jl index f78815a01b866..777af24c779ec 100644 --- a/base/docs/basedocs.jl +++ b/base/docs/basedocs.jl @@ -1970,9 +1970,8 @@ julia> eval(:x) `Symbol`s can also be constructed from strings or other values by calling the constructor `Symbol(x...)`. -`Symbol`s are immutable and should be compared using `===`. -The implementation re-uses the same object for all `Symbol`s with the same name, -so comparison tends to be efficient (it can just compare pointers). +`Symbol`s are immutable and their implementation re-uses the same object for all `Symbol`s +with the same name. Unlike strings, `Symbol`s are "atomic" or "scalar" entities that do not support iteration over characters. From 192e388a39565bc7ebdb4b782da128e1f61a4b67 Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Wed, 11 May 2022 09:55:41 +0200 Subject: [PATCH 03/38] fix same content being shown successively in REPL (#45240) --- stdlib/REPL/src/REPL.jl | 2 +- stdlib/REPL/test/repl.jl | 70 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+), 1 deletion(-) diff --git a/stdlib/REPL/src/REPL.jl b/stdlib/REPL/src/REPL.jl index bf3345f158168..17837a24e3195 100644 --- a/stdlib/REPL/src/REPL.jl +++ b/stdlib/REPL/src/REPL.jl @@ -746,7 +746,7 @@ function history_move_prefix(s::LineEdit.PrefixSearchState, max_idx = length(hist.history)+1 idxs = backwards ? ((cur_idx-1):-1:1) : ((cur_idx+1):1:max_idx) for idx in idxs - if (idx == max_idx) || (startswith(hist.history[idx], prefix) && (hist.history[idx] != cur_response || hist.modes[idx] != LineEdit.mode(s))) + if (idx == max_idx) || (startswith(hist.history[idx], prefix) && (hist.history[idx] != cur_response || get(hist.mode_mapping, hist.modes[idx], nothing) !== LineEdit.mode(s))) m = history_move(s, hist, idx) if m === :ok if idx == max_idx diff --git a/stdlib/REPL/test/repl.jl b/stdlib/REPL/test/repl.jl index f34b00a8f0595..97d9e864f1fe0 100644 --- a/stdlib/REPL/test/repl.jl +++ b/stdlib/REPL/test/repl.jl @@ -1429,3 +1429,73 @@ fake_repl() do stdin_write, stdout_read, repl write(stdin_write, '\x04') Base.wait(repltask) end + +fakehistory_2 = """ +# time: 2014-06-29 20:44:29 EDT +# mode: shell +\txyz = 2 +# time: 2014-06-29 20:44:29 EDT +# mode: julia +\txyz = 2 +# time: 2014-06-29 21:44:29 EDT +# mode: julia +\txyz = 1 +# time: 2014-06-30 17:32:49 EDT +# mode: julia +\tabc = 3 +# time: 2014-06-30 17:32:59 EDT +# mode: julia +\txyz = 1 +# time: 2014-06-30 99:99:99 EDT +# mode: julia +\txyz = 2 +# time: 2014-06-30 99:99:99 EDT +# mode: extended +\tuser imported custom mode +""" + +# Test various history related issues +for prompt = ["TestΠ", () -> randstring(rand(1:10))] + fake_repl() do stdin_write, stdout_read, repl + # In the future if we want we can add a test that the right object + # gets displayed by intercepting the display + repl.specialdisplay = REPL.REPLDisplay(repl) + + errormonitor(@async write(devnull, stdout_read)) # redirect stdout to devnull so we drain the output pipe + + repl.interface = REPL.setup_interface(repl) + repl_mode = repl.interface.modes[1] + shell_mode = repl.interface.modes[2] + help_mode = repl.interface.modes[3] + histp = repl.interface.modes[4] + prefix_mode = repl.interface.modes[5] + + hp = REPL.REPLHistoryProvider(Dict{Symbol,Any}(:julia => repl_mode, + :shell => shell_mode, + :help => help_mode)) + hist_path = tempname() + write(hist_path, fakehistory_2) + REPL.hist_from_file(hp, hist_path) + f = open(hist_path, read=true, write=true, create=true) + hp.history_file = f + seekend(f) + REPL.history_reset_state(hp) + + histp.hp = repl_mode.hist = shell_mode.hist = help_mode.hist = hp + + s = LineEdit.init_state(repl.t, prefix_mode) + prefix_prev() = REPL.history_prev_prefix(s, hp, "x") + prefix_prev() + @test LineEdit.mode(s) == repl_mode + @test buffercontents(LineEdit.buffer(s)) == "xyz = 2" + prefix_prev() + @test LineEdit.mode(s) == repl_mode + @test buffercontents(LineEdit.buffer(s)) == "xyz = 1" + prefix_prev() + @test LineEdit.mode(s) == repl_mode + @test buffercontents(LineEdit.buffer(s)) == "xyz = 2" + prefix_prev() + @test LineEdit.mode(s) == shell_mode + @test buffercontents(LineEdit.buffer(s)) == "xyz = 2" + end +end From 9f7aa7df9dfbfe687e965918ec21d966435991e5 Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Wed, 11 May 2022 09:57:53 +0200 Subject: [PATCH 04/38] handle colors in Dict limit printing (#37568) --- base/dict.jl | 21 ------------------- base/show.jl | 59 ++++++++++++++++++++++++++++++++++++++++++++++++---- test/dict.jl | 20 ++++++++++++++++++ 3 files changed, 75 insertions(+), 25 deletions(-) diff --git a/base/dict.jl b/base/dict.jl index 9cf224f5910a8..22fd8a3a9f844 100644 --- a/base/dict.jl +++ b/base/dict.jl @@ -1,26 +1,5 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license -function _truncate_at_width_or_chars(str, width, chars="", truncmark="…") - truncwidth = textwidth(truncmark) - (width <= 0 || width < truncwidth) && return "" - - wid = truncidx = lastidx = 0 - for (idx, c) in pairs(str) - lastidx = idx - wid += textwidth(c) - wid >= width - truncwidth && truncidx == 0 && (truncidx = lastidx) - (wid >= width || c in chars) && break - end - - lastidx != 0 && str[lastidx] in chars && (lastidx = prevind(str, lastidx)) - truncidx == 0 && (truncidx = lastidx) - if lastidx < lastindex(str) - return String(SubString(str, 1, truncidx) * truncmark) - else - return String(str) - end -end - function show(io::IO, t::AbstractDict{K,V}) where V where K recur_io = IOContext(io, :SHOWN_SET => t, :typeinfo => eltype(t)) diff --git a/base/show.jl b/base/show.jl index 9af8bfbe8a57e..113d3ca786a05 100644 --- a/base/show.jl +++ b/base/show.jl @@ -48,6 +48,57 @@ show(io::IO, ::MIME"text/plain", c::ComposedFunction) = show(io, c) show(io::IO, ::MIME"text/plain", c::Returns) = show(io, c) show(io::IO, ::MIME"text/plain", s::Splat) = show(io, s) +const ansi_regex = r"\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])" +# An iterator similar to `pairs` but skips over "tokens" corresponding to +# ansi sequences +struct IgnoreAnsiIterator + captures::Base.RegexMatchIterator +end +IgnoreAnsiIterator(s::AbstractString) = + IgnoreAnsiIterator(eachmatch(ansi_regex, s)) + +Base.IteratorSize(::Type{IgnoreAnsiIterator}) = Base.SizeUnknown() +function iterate(I::IgnoreAnsiIterator, (i, m_st)=(1, iterate(I.captures))) + # Advance until the next non ansi sequence + if m_st !== nothing + m, j = m_st + if m.offset == i + i += sizeof(m.match) + return iterate(I, (i, iterate(I.captures, j))) + end + end + ci = iterate(I.captures.string, i) + ci === nothing && return nothing + i_prev = i + (c, i) = ci + return (i_prev => c), (i, m_st) +end + +function _truncate_at_width_or_chars(ignore_ansi::Bool, str, width, chars="", truncmark="…") + truncwidth = textwidth(truncmark) + (width <= 0 || width < truncwidth) && return "" + wid = truncidx = lastidx = 0 + ignore_ansi &= match(ansi_regex, str) !== nothing + I = ignore_ansi ? IgnoreAnsiIterator(str) : pairs(str) + for (_lastidx, c) in I + lastidx = _lastidx + wid += textwidth(c) + if wid >= (width - truncwidth) && truncidx == 0 + truncidx = lastidx + end + (wid >= width || c in chars) && break + end + if lastidx != 0 && str[lastidx] in chars + lastidx = prevind(str, lastidx) + end + truncidx == 0 && (truncidx = lastidx) + if lastidx < lastindex(str) + return String(SubString(str, 1, truncidx) * truncmark) + else + return String(str) + end +end + function show(io::IO, ::MIME"text/plain", iter::Union{KeySet,ValueIterator}) isempty(iter) && get(io, :compact, false) && return show(io, iter) summary(io, iter) @@ -71,7 +122,7 @@ function show(io::IO, ::MIME"text/plain", iter::Union{KeySet,ValueIterator}) if limit str = sprint(show, v, context=io, sizehint=0) - str = _truncate_at_width_or_chars(str, cols, "\r\n") + str = _truncate_at_width_or_chars(get(io, :color, false), str, cols, "\r\n") print(io, str) else show(io, v) @@ -129,7 +180,7 @@ function show(io::IO, ::MIME"text/plain", t::AbstractDict{K,V}) where {K,V} end if limit - key = rpad(_truncate_at_width_or_chars(ks[i], keylen, "\r\n"), keylen) + key = rpad(_truncate_at_width_or_chars(get(recur_io, :color, false), ks[i], keylen, "\r\n"), keylen) else key = sprint(show, k, context=recur_io_k, sizehint=0) end @@ -137,7 +188,7 @@ function show(io::IO, ::MIME"text/plain", t::AbstractDict{K,V}) where {K,V} print(io, " => ") if limit - val = _truncate_at_width_or_chars(vs[i], cols - keylen, "\r\n") + val = _truncate_at_width_or_chars(get(recur_io, :color, false), vs[i], cols - keylen, "\r\n") print(io, val) else show(recur_io_v, v) @@ -181,7 +232,7 @@ function show(io::IO, ::MIME"text/plain", t::AbstractSet{T}) where T if limit str = sprint(show, v, context=recur_io, sizehint=0) - print(io, _truncate_at_width_or_chars(str, cols, "\r\n")) + print(io, _truncate_at_width_or_chars(get(io, :color, false), str, cols, "\r\n")) else show(recur_io, v) end diff --git a/test/dict.jl b/test/dict.jl index 3cf5e92ea4251..9695877f44028 100644 --- a/test/dict.jl +++ b/test/dict.jl @@ -368,6 +368,26 @@ end close(io) end + +struct RainBowString + s::String +end + +function Base.show(io::IO, rbs::RainBowString) + for s in rbs.s + _, color = rand(Base.text_colors) + print(io, color, s, "\e[0m") + end +end + +@testset "Display with colors" begin + d = Dict([randstring(8) => [RainBowString(randstring(8)) for i in 1:10] for j in 1:5]...) + str = sprint(io -> show(io, MIME("text/plain"), d); context = (:displaysize=>(30,80), :color=>true, :limit=>true)) + lines = split(str, '\n') + @test all(endswith('…'), lines[2:end]) + @test all(x -> length(x) > 100, lines[2:end]) +end + @testset "Issue #15739" begin # Compact REPL printouts of an `AbstractDict` use brackets when appropriate d = Dict((1=>2) => (3=>45), (3=>10) => (10=>11)) buf = IOBuffer() From c7e7a5d63d3876381ee09727806b8a30cb7809e0 Mon Sep 17 00:00:00 2001 From: Lilith Orion Hafner <60898866+LilithHafner@users.noreply.github.com> Date: Wed, 11 May 2022 02:58:25 -0500 Subject: [PATCH 05/38] Stop using permute!! and invpermute!! (#44941) --- base/combinatorics.jl | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/base/combinatorics.jl b/base/combinatorics.jl index 2dd69fbce4c42..9c753560e3f82 100644 --- a/base/combinatorics.jl +++ b/base/combinatorics.jl @@ -164,8 +164,7 @@ end Permute vector `v` in-place, according to permutation `p`. No checking is done to verify that `p` is a permutation. -To return a new permutation, use `v[p]`. Note that this is generally faster than -`permute!(v,p)` for large vectors. +To return a new permutation, use `v[p]`. Note that this is faster than `permute!(v, p)`. See also [`invpermute!`](@ref). @@ -185,7 +184,7 @@ julia> A 1 ``` """ -permute!(a, p::AbstractVector) = permute!!(a, copymutable(p)) +permute!(v, p::AbstractVector) = (v .= v[p]) function invpermute!!(a, p::AbstractVector{<:Integer}) require_one_based_indexing(a, p) @@ -232,7 +231,7 @@ julia> A 1 ``` """ -invpermute!(a, p::AbstractVector) = invpermute!!(a, copymutable(p)) +invpermute!(v, p::AbstractVector) = (v[p] = v; v) """ invperm(v) From b612159b1121ca16945da06a5b1884d2ab4053c3 Mon Sep 17 00:00:00 2001 From: Lilith Orion Hafner <60898866+LilithHafner@users.noreply.github.com> Date: Wed, 11 May 2022 03:47:40 -0500 Subject: [PATCH 06/38] style & typo in comments (#44938) --- base/sort.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/base/sort.jl b/base/sort.jl index 23579abd77547..395dbbacabdbf 100644 --- a/base/sort.jl +++ b/base/sort.jl @@ -682,7 +682,7 @@ end function radix_sort!(v::AbstractVector{U}, lo::Integer, hi::Integer, bits::Unsigned, t::AbstractVector{U}, chunk_size=radix_chunk_size_heuristic(lo, hi, bits)) where U <: Unsigned # bits is unsigned for performance reasons. - mask = UInt(1) << chunk_size - 0x1 + mask = UInt(1) << chunk_size - 1 counts = Vector{UInt}(undef, mask+2) @inbounds for shift in 0:chunk_size:bits-1 @@ -723,7 +723,7 @@ function radix_chunk_size_heuristic(lo::Integer, hi::Integer, bits::Unsigned) # the chunk size the fewer passes we need. Theoretically, chunk size should be based on # the Lambert W function applied to length. Empirically, we use this heuristic: guess = min(10, log(maybe_unsigned(hi-lo))*3/4+3) - # TODO the maximum chunk size should be based on archetecture cache size. + # TODO the maximum chunk size should be based on architecture cache size. # We need iterations * chunk size ≥ bits, and these cld's # make an effort to get iterations * chunk size ≈ bits From 72794c7cd6fcf6c44f6bd417b2a761482d8bbd58 Mon Sep 17 00:00:00 2001 From: Fons van der Plas Date: Wed, 11 May 2022 10:48:09 +0200 Subject: [PATCH 07/38] docs: `isfile`: direct write instead of IO (#45261) --- base/stat.jl | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/base/stat.jl b/base/stat.jl index f38a82634dc2f..3b6294e65e7f2 100644 --- a/base/stat.jl +++ b/base/stat.jl @@ -353,12 +353,17 @@ Return `true` if `path` is a regular file, `false` otherwise. julia> isfile(homedir()) false -julia> f = open("test_file.txt", "w"); +julia> filename = "test_file.txt"; -julia> isfile(f) +julia> write(filename, "Hello world!"); + +julia> isfile(filename) true -julia> close(f); rm("test_file.txt") +julia> rm(filename); + +julia> isfile(filename) +false ``` See also [`isdir`](@ref) and [`ispath`](@ref). From a734ae4e4aa7fc0aa7dba8d91060fd9462411536 Mon Sep 17 00:00:00 2001 From: Katharine Hyatt Date: Wed, 11 May 2022 04:48:34 -0400 Subject: [PATCH 08/38] Test for setting/getting rounding for F32 and F64 (#45248) --- test/rounding.jl | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test/rounding.jl b/test/rounding.jl index 0fe1513c6c450..508a68032e083 100644 --- a/test/rounding.jl +++ b/test/rounding.jl @@ -341,3 +341,13 @@ end @test f.(a, digits=9, base = 2) == map(x->f(x, digits=9, base = 2), a) end end + +@testset "rounding for F32/F64" begin + for T in [Float32, Float64] + old = rounding(T) + Base.Rounding.setrounding_raw(T, Base.Rounding.JL_FE_TOWARDZERO) + @test rounding(T) == RoundToZero + @test round(T(2.7)) == T(2.0) + Base.Rounding.setrounding_raw(T, Base.Rounding.to_fenv(old)) + end +end From 14232f4b323f3bfbdcca955f339b0aab3957fe5d Mon Sep 17 00:00:00 2001 From: Morten Piibeleht Date: Thu, 12 May 2022 00:04:09 +1200 Subject: [PATCH 09/38] Bump Documenter to 0.27.17 (#45269) --- doc/Manifest.toml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/Manifest.toml b/doc/Manifest.toml index b34ea115c26e9..e3d56b7594251 100644 --- a/doc/Manifest.toml +++ b/doc/Manifest.toml @@ -24,9 +24,9 @@ version = "0.8.6" [[deps.Documenter]] deps = ["ANSIColoredPrinters", "Base64", "Dates", "DocStringExtensions", "IOCapture", "InteractiveUtils", "JSON", "LibGit2", "Logging", "Markdown", "REPL", "Test", "Unicode"] -git-tree-sha1 = "6edbf28671b4df4f692e54ae72f1e35851cfbf38" +git-tree-sha1 = "122d031e8dcb2d3e767ed434bc4d1ae1788b5a7f" uuid = "e30172f5-a6a5-5a46-863b-614d45cd2de4" -version = "0.27.16" +version = "0.27.17" [[deps.IOCapture]] deps = ["Logging", "Random"] @@ -64,9 +64,9 @@ version = "1.2.0" [[deps.Parsers]] deps = ["Dates"] -git-tree-sha1 = "621f4f3b4977325b9128d5fae7a8b4829a0c2222" +git-tree-sha1 = "1285416549ccfcdf0c50d4997a94331e88d68413" uuid = "69de0a69-1ddd-5017-9359-2bf0b02dc9f0" -version = "2.2.4" +version = "2.3.1" [[deps.Printf]] deps = ["Unicode"] From 91ef7f8b47147b8a52b54650fd7aaa2fc91187ea Mon Sep 17 00:00:00 2001 From: Lilith Orion Hafner <60898866+LilithHafner@users.noreply.github.com> Date: Wed, 11 May 2022 07:57:05 -0500 Subject: [PATCH 10/38] use firstindex, lastindex, and eachindex (#45224) --- base/sort.jl | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/base/sort.jl b/base/sort.jl index 395dbbacabdbf..187f8d33b9b81 100644 --- a/base/sort.jl +++ b/base/sort.jl @@ -6,7 +6,7 @@ import ..@__MODULE__, ..parentmodule const Base = parentmodule(@__MODULE__) using .Base.Order using .Base: copymutable, LinearIndices, length, (:), iterate, - eachindex, axes, first, last, similar, zip, OrdinalRange, + eachindex, axes, first, last, similar, zip, OrdinalRange, firstindex, lastindex, AbstractVector, @inbounds, AbstractRange, @eval, @inline, Vector, @noinline, AbstractMatrix, AbstractUnitRange, isless, identity, eltype, >, <, <=, >=, |, +, -, *, !, extrema, sub_with_overflow, add_with_overflow, oneunit, div, getindex, setindex!, @@ -94,8 +94,7 @@ issorted(itr; issorted(itr, ord(lt,by,rev,order)) function partialsort!(v::AbstractVector, k::Union{Integer,OrdinalRange}, o::Ordering) - inds = axes(v, 1) - sort!(v, first(inds), last(inds), PartialQuickSort(k), o) + sort!(v, firstindex(v), lastindex(v), PartialQuickSort(k), o) maybeview(v, k) end @@ -293,7 +292,7 @@ searchsorted(a::AbstractRange{<:Real}, x::Real, o::DirectOrdering) = for s in [:searchsortedfirst, :searchsortedlast, :searchsorted] @eval begin - $s(v::AbstractVector, x, o::Ordering) = (inds = axes(v, 1); $s(v,x,first(inds),last(inds),o)) + $s(v::AbstractVector, x, o::Ordering) = $s(v,x,firstindex(v),lastindex(v),o) $s(v::AbstractVector, x; lt=isless, by=identity, rev::Union{Bool,Nothing}=nothing, order::Ordering=Forward) = $s(v,x,ord(lt,by,rev,order)) @@ -862,8 +861,7 @@ defalg(v::AbstractArray{Missing}) = DEFAULT_UNSTABLE # for method disambiguation defalg(v::AbstractArray{Union{}}) = DEFAULT_UNSTABLE # for method disambiguation function sort!(v::AbstractVector, alg::Algorithm, order::Ordering) - inds = axes(v,1) - sort!(v,first(inds),last(inds),alg,order) + sort!(v,firstindex(v),lastindex(v),alg,order) end """ @@ -1055,7 +1053,7 @@ function partialsortperm!(ix::AbstractVector{<:Integer}, v::AbstractVector, "same length/indices as the source vector, $(axes(ix,1)) != $(axes(v,1))")) end if !initialized - @inbounds for i = axes(ix,1) + @inbounds for i in eachindex(ix) ix[i] = i end end @@ -1156,7 +1154,7 @@ function sortperm!(x::AbstractVector{<:Integer}, v::AbstractVector; throw(ArgumentError("index vector must have the same length/indices as the source vector, $(axes(x,1)) != $(axes(v,1))")) end if !initialized - @inbounds for i = axes(v,1) + @inbounds for i in eachindex(v) x[i] = i end end @@ -1393,7 +1391,7 @@ end module Float using ..Sort using ...Order -using ..Base: @inbounds, AbstractVector, Vector, last, axes, Missing, Type, reinterpret +using ..Base: @inbounds, AbstractVector, Vector, last, firstindex, lastindex, Missing, Type, reinterpret import Core.Intrinsics: slt_int import ..Sort: sort!, UIntMappable, uint_map, uint_unmap @@ -1445,7 +1443,7 @@ allowsmissing(::AbstractVector{<:Integer}, T >: Missing function specials2left!(testf::Function, v::AbstractVector, o::Ordering, - lo::Integer=first(axes(v,1)), hi::Integer=last(axes(v,1))) + lo::Integer=firstindex(v), hi::Integer=lastindex(v)) i = lo @inbounds while i <= hi && testf(o,v[i]) i += 1 @@ -1461,7 +1459,7 @@ function specials2left!(testf::Function, v::AbstractVector, o::Ordering, return i, hi end function specials2right!(testf::Function, v::AbstractVector, o::Ordering, - lo::Integer=first(axes(v,1)), hi::Integer=last(axes(v,1))) + lo::Integer=firstindex(v), hi::Integer=lastindex(v)) i = hi @inbounds while lo <= i && testf(o,v[i]) i -= 1 @@ -1478,7 +1476,7 @@ function specials2right!(testf::Function, v::AbstractVector, o::Ordering, end function specials2left!(v::AbstractVector, a::Algorithm, o::Ordering) - lo, hi = first(axes(v,1)), last(axes(v,1)) + lo, hi = firstindex(v), lastindex(v) if allowsmissing(v, o) i, _ = specials2left!((v, o) -> ismissing(v, o) || isnan(v, o), v, o, lo, hi) sort!(v, lo, i-1, a, o) @@ -1488,7 +1486,7 @@ function specials2left!(v::AbstractVector, a::Algorithm, o::Ordering) end end function specials2right!(v::AbstractVector, a::Algorithm, o::Ordering) - lo, hi = first(axes(v,1)), last(axes(v,1)) + lo, hi = firstindex(v), lastindex(v) if allowsmissing(v, o) _, i = specials2right!((v, o) -> ismissing(v, o) || isnan(v, o), v, o, lo, hi) sort!(v, i+1, hi, a, o) @@ -1514,7 +1512,7 @@ issignleft(o::Perm, i::Integer) = issignleft(o.order, o.data[i]) function fpsort!(v::AbstractVector, a::Algorithm, o::Ordering) # fpsort!'s optimizations speed up comparisons, of which there are O(nlogn). # The overhead is O(n). For n < 10, it's not worth it. - length(v) < 10 && return sort!(v, first(axes(v,1)), last(axes(v,1)), SMALL_ALGORITHM, o) + length(v) < 10 && return sort!(v, firstindex(v), lastindex(v), SMALL_ALGORITHM, o) i, j = lo, hi = specials2end!(v,a,o) @inbounds while true @@ -1531,7 +1529,7 @@ end fpsort!(v::AbstractVector, a::Sort.PartialQuickSort, o::Ordering) = - sort!(v, first(axes(v,1)), last(axes(v,1)), a, o) + sort!(v, firstindex(v), lastindex(v), a, o) sort!(v::FPSortable, a::Algorithm, o::DirectOrdering) = fpsort!(v, a, o) From 88052c91d6561ce085c6db52c59389183721418a Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Wed, 11 May 2022 15:02:20 +0200 Subject: [PATCH 11/38] rewrite the C/Fortran calling manual to use `@ccall` as the "first class" way of calling (#45206) --- doc/src/manual/calling-c-and-fortran-code.md | 379 ++++++++++--------- 1 file changed, 192 insertions(+), 187 deletions(-) diff --git a/doc/src/manual/calling-c-and-fortran-code.md b/doc/src/manual/calling-c-and-fortran-code.md index 5529018217c1a..0ebed7db009c9 100644 --- a/doc/src/manual/calling-c-and-fortran-code.md +++ b/doc/src/manual/calling-c-and-fortran-code.md @@ -4,8 +4,8 @@ Though most code can be written in Julia, there are many high-quality, mature li computing already written in C and Fortran. To allow easy use of this existing code, Julia makes it simple and efficient to call C and Fortran functions. Julia has a "no boilerplate" philosophy: functions can be called directly from Julia without any "glue" code, code generation, or compilation --- even from the interactive prompt. This is accomplished just by making an appropriate call with -[`ccall`](@ref) syntax, which looks like an ordinary function call. +-- even from the interactive prompt. This is accomplished just by making an appropriate call with the +[`@ccall`](@ref) macro (or the less convenient [`ccall`](@ref) syntax, see the [`ccall` syntax section](@ref ccall-interface)). The code to be called must be available as a shared library. Most C and Fortran libraries ship compiled as shared libraries already, but if you are compiling the code yourself using GCC (or @@ -13,50 +13,34 @@ Clang), you will need to use the `-shared` and `-fPIC` options. The machine inst by Julia's JIT are the same as a native C call would be, so the resulting overhead is the same as calling a library function from C code. [^1] -Shared libraries and functions are referenced by a tuple of the form `(:function, "library")` -or `("function", "library")` where `function` is the C-exported function name, and `library` refers -to the shared library name. Shared libraries available in the (platform-specific) load path will -be resolved by name. The full path to the library may also be specified. - -A function name may be used alone in place of the tuple (just `:function` or `"function"`). In -this case the name is resolved within the current process. This form can be used to call C library -functions, functions in the Julia runtime, or functions in an application linked to Julia. - By default, Fortran compilers [generate mangled names](https://en.wikipedia.org/wiki/Name_mangling#Fortran) (for example, converting function names to lowercase or uppercase, often appending an -underscore), and so to call a Fortran function via [`ccall`](@ref) you must pass +underscore), and so to call a Fortran function you must pass the mangled identifier corresponding to the rule followed by your Fortran -compiler. Also, when calling a Fortran function, all inputs must be passed as +compiler. Also, when calling a Fortran function, all inputs must be passed as pointers to allocated values on the heap or stack. This applies not only to arrays and other mutable objects which are normally heap-allocated, but also to scalar values such as integers and floats which are normally stack-allocated and commonly passed in registers when using C or Julia calling conventions. -Finally, you can use [`ccall`](@ref) to actually generate a call to the library function. The arguments -to [`ccall`](@ref) are: - -1. A `(:function, "library")` pair (most common), - - OR - - a `:function` name symbol or `"function"` name string (for symbols in the current process or libc), - - OR - - a function pointer (for example, from `dlsym`). - -2. The function's return type - -3. A tuple of input types, corresponding to the function signature - -4. The actual argument values to be passed to the function, if any; each is a separate parameter. +The syntax for [`@ccall`](@ref) to generate a call to the library function is: -!!! note - The `(:function, "library")` pair, return type, and input types must be literal constants - (i.e., they can't be variables, but see [Non-constant Function Specifications](@ref) below). +```julia + @ccall library.function_name(argvalue1::argtype1, ...)::returntype + @ccall function_name(argvalue1::argtype1, ...)::returntype + @ccall $function_pointer(argvalue1::argtype1, ...)::returntype +``` - The remaining parameters are evaluated at compile time, when the containing method is defined. +where `library` is a string constant or literal (but see [Non-constant Function +Specifications](@ref) below). The library may be omitted, in which case the +function name is resolved in the current process. This form can be used to call +C library functions, functions in the Julia runtime, or functions in an +application linked to Julia. The full path to the library may also be specified. +Alternatively, `@ccall` may also be used to call a function pointer +`$function_pointer`, such as one returned by `Libdl.dlsym`. The `argtype`s +corresponds to the C-function signature and the `argvalue`s are the actual +argument values to be passed to the function. !!! note See below for how to [map C types to Julia types](@ref mapping-c-types-to-julia). @@ -65,41 +49,25 @@ As a complete but simple example, the following calls the `clock` function from library on most Unix-derived systems: ```julia-repl -julia> t = ccall(:clock, Int32, ()) -2292761 - -julia> t +julia> t = @ccall clock()::Int32 2292761 julia> typeof(t) Int32 ``` -`clock` takes no arguments and returns an [`Int32`](@ref). One common mistake is forgetting that a 1-tuple of -argument types must be written with a trailing comma. For example, to call the `getenv` function +`clock` takes no arguments and returns an `Int32`. To call the `getenv` function to get a pointer to the value of an environment variable, one makes a call like this: ```julia-repl -julia> path = ccall(:getenv, Cstring, (Cstring,), "SHELL") +julia> path = @ccall getenv("SHELL"::Cstring)::Cstring Cstring(@0x00007fff5fbffc45) julia> unsafe_string(path) "/bin/bash" ``` -Note that the argument type tuple must be written as `(Cstring,)`, not `(Cstring)`. This -is because `(Cstring)` is just the expression `Cstring` surrounded by parentheses, rather than -a 1-tuple containing `Cstring`: - -```jldoctest -julia> (Cstring) -Cstring - -julia> (Cstring,) -(Cstring,) -``` - -In practice, especially when providing reusable functionality, one generally wraps [`ccall`](@ref) +In practice, especially when providing reusable functionality, one generally wraps `@ccall` uses in Julia functions that set up arguments and then check for errors in whatever manner the C or Fortran function specifies. And if an error occurs it is thrown as a normal Julia exception. This is especially important since C and Fortran APIs are notoriously inconsistent about how they indicate error @@ -108,7 +76,7 @@ which is a simplified version of the actual definition from [`env.jl`](https://g ```julia function getenv(var::AbstractString) - val = ccall(:getenv, Cstring, (Cstring,), var) + val = @ccall getenv(var::Cstring)::Cstring if val == C_NULL error("getenv: undefined variable: ", var) end @@ -116,9 +84,9 @@ function getenv(var::AbstractString) end ``` -The C `getenv` function indicates an error by returning `NULL`, but other standard C functions -indicate errors in various different ways, including by returning -1, 0, 1 and other special values. -This wrapper throws an exception clearly indicating the problem if the caller tries to get a non-existent +The C `getenv` function indicates an error by returning `C_NULL`, but other standard C functions +indicate errors in different ways, including by returning -1, 0, 1, and other special values. +This wrapper throws an exception indicating the problem if the caller tries to get a non-existent environment variable: ```julia-repl @@ -126,20 +94,15 @@ julia> getenv("SHELL") "/bin/bash" julia> getenv("FOOBAR") -getenv: undefined variable: FOOBAR +ERROR: getenv: undefined variable: FOOBAR ``` Here is a slightly more complex example that discovers the local machine's hostname. -In this example, the networking library code is assumed to be in a shared library named "libc". -In practice, this function is usually part of the C standard library, and so the "libc" -portion should be omitted, but we wish to show here the usage of this syntax. ```julia function gethostname() hostname = Vector{UInt8}(undef, 256) # MAXHOSTNAMELEN - err = ccall((:gethostname, "libc"), Int32, - (Ptr{UInt8}, Csize_t), - hostname, sizeof(hostname)) + err = @ccall gethostname(hostname::Ptr{UInt8}, sizeof(hostname)::Csize_t)::Int32 Base.systemerror("gethostname", err != 0) hostname[end] = 0 # ensure null-termination return GC.@preserve hostname unsafe_string(pointer(hostname)) @@ -148,19 +111,39 @@ end This example first allocates an array of bytes. It then calls the C library function `gethostname` to populate the array with the hostname. Finally, it takes a pointer to the hostname buffer, and -converts the pointer to a Julia string, assuming that it is a NUL-terminated C string. +converts the pointer to a Julia string, assuming that it is a null-terminated C string. It is common for C libraries to use this pattern of requiring the caller to allocate memory to be passed to the callee and populated. Allocation of memory from Julia like this is generally accomplished by creating an uninitialized array and passing a pointer to its data to the C function. This is why we don't use the `Cstring` type here: as the array is uninitialized, it could contain -NUL bytes. Converting to a `Cstring` as part of the [`ccall`](@ref) checks for contained NUL bytes +null bytes. Converting to a `Cstring` as part of the `@ccall` checks for contained null bytes and could therefore throw a conversion error. Dereferencing `pointer(hostname)` with `unsafe_string` is an unsafe operation as it requires access to the memory allocated for `hostname` that may have been in the meanwhile garbage collected. The macro [`GC.@preserve`](@ref) prevents this from happening and therefore accessing an invalid memory location. +Finally, here is an example of specifying a library via a path. +We create a shared library with the following content + +```c +#include + +void say_y(int y) +{ + printf("Hello from C: got y = %d.\n", y); +} +``` + +and compile it with `gcc -fPIC -shared -o mylib.so mylib.c`. +It can then be called by specifying the (absolute) path as the library name: + +```julia-repl +julia> @ccall "./mylib.so".say_y(5::Cint)::Cvoid +Hello from C: got y = 5. +``` + ## Creating C-Compatible Julia Function Pointers It is possible to pass Julia functions to native C functions that accept function pointer arguments. @@ -178,7 +161,7 @@ Julia function. The arguments to [`@cfunction`](@ref) are: 3. A tuple of input types, corresponding to the function signature !!! note - As with `ccall`, the return type and tuple of input types must be literal constants. + As with `@ccall`, the return type and the input types must be literal constants. !!! note Currently, only the platform-default C calling convention is supported. This means that @@ -193,11 +176,11 @@ Julia function. The arguments to [`@cfunction`](@ref) are: A classic example is the standard C library `qsort` function, declared as: ```c -void qsort(void *base, size_t nmemb, size_t size, +void qsort(void *base, size_t nitems, size_t size, int (*compare)(const void*, const void*)); ``` -The `base` argument is a pointer to an array of length `nmemb`, with elements of `size` bytes +The `base` argument is a pointer to an array of length `nitems`, with elements of `size` bytes each. `compare` is a callback function which takes pointers to two elements `a` and `b` and returns an integer less/greater than zero if `a` should appear before/after `b` (or zero if any order is permitted). @@ -209,8 +192,7 @@ calling `qsort` and passing arguments, we need to write a comparison function: ```jldoctest mycompare julia> function mycompare(a, b)::Cint return (a < b) ? -1 : ((a > b) ? +1 : 0) - end -mycompare (generic function with 1 method) + end; ``` `qsort` expects a comparison function that return a C `int`, so we annotate the return type @@ -229,15 +211,9 @@ julia> mycompare_c = @cfunction(mycompare, Cint, (Ref{Cdouble}, Ref{Cdouble})); The final call to `qsort` looks like this: ```jldoctest mycompare -julia> A = [1.3, -2.7, 4.4, 3.1] -4-element Vector{Float64}: - 1.3 - -2.7 - 4.4 - 3.1 +julia> A = [1.3, -2.7, 4.4, 3.1]; -julia> ccall(:qsort, Cvoid, (Ptr{Cdouble}, Csize_t, Csize_t, Ptr{Cvoid}), - A, length(A), sizeof(eltype(A)), mycompare_c) +julia> @ccall qsort(A::Ptr{Cdouble}, length(A)::Csize_t, sizeof(eltype(A))::Csize_t, mycompare_c::Ptr{Cvoid})::Cvoid julia> A 4-element Vector{Float64}: @@ -271,15 +247,16 @@ Julia automatically inserts calls to the [`Base.cconvert`](@ref) function to con to the specified type. For example, the following call: ```julia -ccall((:foo, "libfoo"), Cvoid, (Int32, Float64), x, y) +@ccall "libfoo".foo(x::Int32, y::Float64)::Cvoid ``` will behave as if it were written like this: ```julia -ccall((:foo, "libfoo"), Cvoid, (Int32, Float64), - Base.unsafe_convert(Int32, Base.cconvert(Int32, x)), - Base.unsafe_convert(Float64, Base.cconvert(Float64, y))) +@ccall "libfoo".foo( + Base.unsafe_convert(Int32, Base.cconvert(Int32, x))::Int32, + Base.unsafe_convert(Float64, Base.cconvert(Float64, y))::Float64 + )::Cvoid ``` [`Base.cconvert`](@ref) normally just calls [`convert`](@ref), but can be defined to return an @@ -345,7 +322,7 @@ same: that the element type of the array matches `T`, and the address of the first element is passed. Therefore, if an `Array` contains data in the wrong format, it will have to be explicitly converted - using a call such as `trunc(Int32, a)`. + using a call such as `trunc.(Int32, A)`. To pass an array `A` as a pointer of a different type *without* converting the data beforehand (for example, to pass a `Float64` array to a function that operates on uninterpreted bytes), you @@ -387,7 +364,7 @@ an `Int` in Julia). | `void` and `[[noreturn]]` or `_Noreturn` | | | `Union{}` | | `void*` | | | `Ptr{Cvoid}` (or similarly `Ref{Cvoid}`) | | `T*` (where T represents an appropriately defined type) | | | `Ref{T}` (T may be safely mutated only if T is an isbits type) | -| `char*` (or `char[]`, e.g. a string) | `CHARACTER*N` | | `Cstring` if NUL-terminated, or `Ptr{UInt8}` if not | +| `char*` (or `char[]`, e.g. a string) | `CHARACTER*N` | | `Cstring` if null-terminated, or `Ptr{UInt8}` if not | | `char**` (or `*char[]`) | | | `Ptr{Ptr{UInt8}}` | | `jl_value_t*` (any Julia Type) | | | `Any` | | `jl_value_t* const*` (a reference to a Julia value) | | | `Ref{Any}` (const, since mutation would require a write barrier, which is not possible to insert correctly) | @@ -396,13 +373,13 @@ an `Int` in Julia). | `...` (variadic function specification) | | | `; va_arg1::T, va_arg2::S, etc.` (only supported with `@ccall` macro) | The [`Cstring`](@ref) type is essentially a synonym for `Ptr{UInt8}`, except the conversion to `Cstring` -throws an error if the Julia string contains any embedded NUL characters (which would cause the -string to be silently truncated if the C routine treats NUL as the terminator). If you are passing -a `char*` to a C routine that does not assume NUL termination (e.g. because you pass an explicit -string length), or if you know for certain that your Julia string does not contain NUL and want +throws an error if the Julia string contains any embedded null characters (which would cause the +string to be silently truncated if the C routine treats null as the terminator). If you are passing +a `char*` to a C routine that does not assume null termination (e.g. because you pass an explicit +string length), or if you know for certain that your Julia string does not contain null and want to skip the check, you can use `Ptr{UInt8}` as the argument type. `Cstring` can also be used as the [`ccall`](@ref) return type, but in that case it obviously does not introduce any extra -checks and is only meant to improve readability of the call. +checks and is only meant to improve the readability of the call. **System Dependent Types** @@ -419,26 +396,26 @@ checks and is only meant to improve readability of the call. `Ref{..}` wrapper around their type specification. !!! warning - For string arguments (`char*`) the Julia type should be `Cstring` (if NUL- terminated data is + For string arguments (`char*`) the Julia type should be `Cstring` (if null-terminated data is expected), or either `Ptr{Cchar}` or `Ptr{UInt8}` otherwise (these two pointer types have the same effect), as described above, not `String`. Similarly, for array arguments (`T[]` or `T*`), the Julia type should again be `Ptr{T}`, not `Vector{T}`. !!! warning - Julia's `Char` type is 32 bits, which is not the same as the wide character type (`wchar_t` or + Julia's `Char` type is 32 bits, which is not the same as the wide-character type (`wchar_t` or `wint_t`) on all platforms. !!! warning A return type of `Union{}` means the function will not return, i.e., C++11 `[[noreturn]]` or C11 `_Noreturn` (e.g. `jl_throw` or `longjmp`). Do not use this for functions that return no value - (`void`) but do return, use `Cvoid` instead. + (`void`) but do return, for those, use `Cvoid` instead. !!! note For `wchar_t*` arguments, the Julia type should be [`Cwstring`](@ref) (if the C routine expects a - NUL-terminated string), or `Ptr{Cwchar_t}` otherwise. Note also that UTF-8 string data in Julia is - internally NUL-terminated, so it can be passed to C functions expecting NUL-terminated data without + null-terminated string), or `Ptr{Cwchar_t}` otherwise. Note also that UTF-8 string data in Julia is + internally null-terminated, so it can be passed to C functions expecting null-terminated data without making a copy (but using the `Cwstring` type will cause an error to be thrown if the string itself - contains NUL characters). + contains null characters). !!! note C functions that take an argument of type `char**` can be called by using a `Ptr{Ptr{UInt8}}` @@ -452,7 +429,7 @@ checks and is only meant to improve readability of the call. ```julia argv = [ "a.out", "arg1", "arg2" ] - ccall(:main, Int32, (Int32, Ptr{Ptr{UInt8}}), length(argv), argv) + @ccall main(length(argv)::Int32, argv::Ptr{Ptr{UInt8}})::Int32 ``` !!! note @@ -481,7 +458,7 @@ checks and is only meant to improve readability of the call. Fortran compilers *may* also add other hidden arguments for pointers, assumed-shape (`:`) and assumed-size (`*`) arrays. Such behaviour can be avoided by using `ISO_C_BINDING` and including `bind(c)` in the definition of the subroutine, which is strongly recommended for - interoperable code. In this case there will be no hidden arguments, at the cost of some + interoperable code. In this case, there will be no hidden arguments, at the cost of some language features (e.g. only `character(len=1)` will be permitted to pass strings). !!! note @@ -505,7 +482,7 @@ You can get an approximation of a `union` if you know, a priori, the field that the greatest size (potentially including padding). When translating your fields to Julia, declare the Julia field to be only of that type. -Arrays of parameters can be expressed with `NTuple`. For example, the struct in C notation written as +Arrays of parameters can be expressed with `NTuple`. For example, the struct in C notation is written as ```c struct B { @@ -546,7 +523,7 @@ unsafe_string(str + Core.sizeof(Cint), len) ### Type Parameters -The type arguments to `ccall` and `@cfunction` are evaluated statically, +The type arguments to `@ccall` and `@cfunction` are evaluated statically, when the method containing the usage is defined. They therefore must take the form of a literal tuple, not a variable, and cannot reference local variables. @@ -559,9 +536,9 @@ However, while the type layout must be known statically to compute the intended the static parameters of the function are considered to be part of this static environment. The static parameters of the function may be used as type parameters in the call signature, as long as they don't affect the layout of the type. -For example, `f(x::T) where {T} = ccall(:valid, Ptr{T}, (Ptr{T},), x)` +For example, `f(x::T) where {T} = @ccall valid(x::Ptr{T})::Ptr{T}` is valid, since `Ptr` is always a word-size primitive type. -But, `g(x::T) where {T} = ccall(:notvalid, T, (T,), x)` +But, `g(x::T) where {T} = @ccall notvalid(x::T)::T` is not valid, since the type layout of `T` is not known statically. ### SIMD Values @@ -569,7 +546,7 @@ is not valid, since the type layout of `T` is not known statically. Note: This feature is currently implemented on 64-bit x86 and AArch64 platforms only. If a C/C++ routine has an argument or return value that is a native SIMD type, the corresponding -Julia type is a homogeneous tuple of `VecElement` that naturally maps to the SIMD type. Specifically: +Julia type is a homogeneous tuple of `VecElement` that naturally maps to the SIMD type. Specifically: > * The tuple must be the same size as the SIMD type. For example, a tuple representing an `__m128` > on x86 must have a size of 16 bytes. @@ -596,18 +573,18 @@ a = m256(ntuple(i -> VecElement(sin(Float32(i))), 8)) b = m256(ntuple(i -> VecElement(cos(Float32(i))), 8)) function call_dist(a::m256, b::m256) - ccall((:dist, "libdist"), m256, (m256, m256), a, b) + @ccall "libdist".dist(a::m256, b::m256)::m256 end println(call_dist(a,b)) ``` -The host machine must have the requisite SIMD registers. For example, the code above will not +The host machine must have the requisite SIMD registers. For example, the code above will not work on hosts without AVX support. ### Memory Ownership -**malloc/free** +**`malloc`/`free`** Memory allocation and deallocation of such objects must be handled by calls to the appropriate cleanup routines in the libraries being used, just like in any C program. Do not try to free an @@ -615,13 +592,13 @@ object received from a C library with [`Libc.free`](@ref) in Julia, as this may being called via the wrong library and cause the process to abort. The reverse (passing an object allocated in Julia to be freed by an external library) is equally invalid. -### When to use T, Ptr{T} and Ref{T} +### When to use `T`, `Ptr{T}` and `Ref{T}` In Julia code wrapping calls to external C routines, ordinary (non-pointer) data should be declared -to be of type `T` inside the [`ccall`](@ref), as they are passed by value. For C code accepting +to be of type `T` inside the `@ccall`, as they are passed by value. For C code accepting pointers, [`Ref{T}`](@ref) should generally be used for the types of input arguments, allowing the use of pointers to memory managed by either Julia or C through the implicit call to [`Base.cconvert`](@ref). -In contrast, pointers returned by the C function called should be declared to be of output type +In contrast, pointers returned by the C function called should be declared to be of the output type [`Ptr{T}`](@ref), reflecting that the memory pointed to is managed by C only. Pointers contained in C structs should be represented as fields of type `Ptr{T}` within the corresponding Julia struct types designed to mimic the internal structure of corresponding C structs. @@ -633,7 +610,7 @@ Fortran subroutines, or a `T` for Fortran functions returning the type `T`. ## Mapping C Functions to Julia -### `ccall` / `@cfunction` argument translation guide +### `@ccall` / `@cfunction` argument translation guide For translating a C argument list to Julia: @@ -651,7 +628,7 @@ For translating a C argument list to Julia: * depends on how this parameter is used, first translate this to the intended pointer type, then determine the Julia equivalent using the remaining rules in this list - * this argument may be declared as `Ptr{Cvoid}`, if it really is just an unknown pointer + * this argument may be declared as `Ptr{Cvoid}` if it really is just an unknown pointer * `jl_value_t*` * `Any` @@ -683,7 +660,7 @@ For translating a C argument list to Julia: * not supported by `ccall` or `@cfunction` -### `ccall` / `@cfunction` return type translation guide +### `@ccall` / `@cfunction` return type translation guide For translating a C return type to Julia: @@ -704,7 +681,7 @@ For translating a C return type to Julia: * depends on how this parameter is used, first translate this to the intended pointer type, then determine the Julia equivalent using the remaining rules in this list - * this argument may be declared as `Ptr{Cvoid}`, if it really is just an unknown pointer + * this argument may be declared as `Ptr{Cvoid}` if it really is just an unknown pointer * `jl_value_t*` * `Any` @@ -725,20 +702,20 @@ For translating a C return type to Julia: * `Ptr{T}`, where `T` is the Julia type corresponding to `T` * `T (*)(...)` (e.g. a pointer to a function) - * `Ptr{Cvoid}` to call this directly from Julia you will need to pass this as the first argument to [`ccall`](@ref). + * `Ptr{Cvoid}` to call this directly from Julia you will need to pass this as the first argument to `@ccall`. See [Indirect Calls](@ref). ### Passing Pointers for Modifying Inputs Because C doesn't support multiple return values, often C functions will take pointers to data -that the function will modify. To accomplish this within a [`ccall`](@ref), you need to first +that the function will modify. To accomplish this within a `@ccall`, you need to first encapsulate the value inside a [`Ref{T}`](@ref) of the appropriate type. When you pass this `Ref` object as an argument, Julia will automatically pass a C pointer to the encapsulated data: ```julia width = Ref{Cint}(0) range = Ref{Cfloat}(0) -ccall(:foo, Cvoid, (Ref{Cint}, Ref{Cfloat}), width, range) +@ccall foo(width::Ref{Cint}, range::Ref{Cfloat})::Cvoid ``` Upon return, the contents of `width` and `range` can be retrieved (if they were changed by `foo`) @@ -755,12 +732,7 @@ end # The corresponding C signature is # gsl_permutation * gsl_permutation_alloc (size_t n); function permutation_alloc(n::Integer) - output_ptr = ccall( - (:gsl_permutation_alloc, :libgsl), # name of C function and library - Ptr{gsl_permutation}, # output type - (Csize_t,), # tuple of input types - n # name of Julia variable to pass in - ) + output_ptr = @ccall "libgsl".gsl_permutation_alloc(n::Csize_t)::Ptr{gsl_permutation} if output_ptr == C_NULL # Could not allocate memory throw(OutOfMemoryError()) end @@ -773,13 +745,13 @@ through `:libgsl`) defines an opaque pointer, `gsl_permutation *`, as the return function `gsl_permutation_alloc`. As user code never has to look inside the `gsl_permutation` struct, the corresponding Julia wrapper simply needs a new type declaration, `gsl_permutation`, that has no internal fields and whose sole purpose is to be placed in the type parameter of a -`Ptr` type. The return type of the [`ccall`](@ref) is declared as `Ptr{gsl_permutation}`, since +`Ptr` type. The return type of the [`ccall`](@ref) is declared as `Ptr{gsl_permutation}`, since the memory allocated and pointed to by `output_ptr` is controlled by C. The input `n` is passed by value, and so the function's input signature is -simply declared as `(Csize_t,)` without any `Ref` or `Ptr` necessary. (If the +simply declared as `::Csize_t` without any `Ref` or `Ptr` necessary. (If the wrapper was calling a Fortran function instead, the corresponding function input -signature would instead be `(Ref{Csize_t},)`, since Fortran variables are +signature would instead be `::Ref{Csize_t}`, since Fortran variables are passed by pointers.) Furthermore, `n` can be any type that is convertible to a `Csize_t` integer; the [`ccall`](@ref) implicitly calls [`Base.cconvert(Csize_t, n)`](@ref). @@ -789,29 +761,11 @@ Here is a second example wrapping the corresponding destructor: ```julia # The corresponding C signature is # void gsl_permutation_free (gsl_permutation * p); -function permutation_free(p::Ref{gsl_permutation}) - ccall( - (:gsl_permutation_free, :libgsl), # name of C function and library - Cvoid, # output type - (Ref{gsl_permutation},), # tuple of input types - p # name of Julia variable to pass in - ) +function permutation_free(p::Ptr{gsl_permutation}) + @ccall "libgsl".gsl_permutation_free(p::Ptr{gsl_permutation})::Cvoid end ``` -Here, the input `p` is declared to be of type `Ref{gsl_permutation}`, meaning that the memory -that `p` points to may be managed by Julia or by C. A pointer to memory allocated by C should -be of type `Ptr{gsl_permutation}`, but it is convertible using [`Base.cconvert`](@ref) and therefore - -Now if you look closely enough at this example, you may notice that it is incorrect, given our explanation -above of preferred declaration types. Do you see it? The function we are calling is going to free the -memory. This type of operation cannot be given a Julia object (it will crash or cause memory corruption). -Therefore, it may be preferable to declare the `p` type as `Ptr{gsl_permutation }`, to make it harder for the -user to mistakenly pass another sort of object there than one obtained via `gsl_permutation_alloc`. - -If the C wrapper never expects the user to pass pointers to memory managed by Julia, then using -`p::Ptr{gsl_permutation}` for the method signature of the wrapper and similarly in the [`ccall`](@ref) -is also acceptable. Here is a third example passing Julia arrays: @@ -824,12 +778,8 @@ function sf_bessel_Jn_array(nmin::Integer, nmax::Integer, x::Real) throw(DomainError()) end result_array = Vector{Cdouble}(undef, nmax - nmin + 1) - errorcode = ccall( - (:gsl_sf_bessel_Jn_array, :libgsl), # name of C function and library - Cint, # output type - (Cint, Cint, Cdouble, Ref{Cdouble}),# tuple of input types - nmin, nmax, x, result_array # names of Julia variables to pass in - ) + errorcode = @ccall "libgsl".gsl_sf_bessel_Jn_array( + nmin::Cint, nmax::Cint, x::Cdouble, result_array::Ref{Cdouble})::Cint if errorcode != 0 error("GSL error code $errorcode") end @@ -846,9 +796,9 @@ the Julia pointer to a Julia array data structure into a form understandable by ## Fortran Wrapper Example The following example utilizes `ccall` to call a function in a common Fortran library (libBLAS) to -computes a dot product. Notice that the argument mapping is a bit different here than above, as -we need to map from Julia to Fortran. On every argument type, we specify `Ref` or `Ptr`. This -mangling convention may be specific to your fortran compiler and operating system, and is likely +compute a dot product. Notice that the argument mapping is a bit different here than above, as +we need to map from Julia to Fortran. On every argument type, we specify `Ref` or `Ptr`. This +mangling convention may be specific to your Fortran compiler and operating system and is likely undocumented. However, wrapping each in a `Ref` (or `Ptr`, where equivalent) is a frequent requirement of Fortran compiler implementations: @@ -857,10 +807,8 @@ function compute_dot(DX::Vector{Float64}, DY::Vector{Float64}) @assert length(DX) == length(DY) n = length(DX) incx = incy = 1 - product = ccall((:ddot_, "libLAPACK"), - Float64, - (Ref{Int32}, Ptr{Float64}, Ref{Int32}, Ptr{Float64}, Ref{Int32}), - n, DX, incx, DY, incy) + product = @ccall "libLAPACK".ddot( + n::Ref{Int32}, DX::Ptr{Float64}, incx::Ref{Int32}, DY::Ptr{Float64}, incy::Ref{Int32})::Float64 return product end ``` @@ -868,12 +816,12 @@ end ## Garbage Collection Safety -When passing data to a [`ccall`](@ref), it is best to avoid using the [`pointer`](@ref) function. -Instead define a convert method and pass the variables directly to the [`ccall`](@ref). [`ccall`](@ref) +When passing data to a `@ccall`, it is best to avoid using the [`pointer`](@ref) function. +Instead define a convert method and pass the variables directly to the `@ccall`. `@ccall` automatically arranges that all of its arguments will be preserved from garbage collection until -the call returns. If a C API will store a reference to memory allocated by Julia, after the [`ccall`](@ref) +the call returns. If a C API will store a reference to memory allocated by Julia, after the `@ccall` returns, you must ensure that the object remains visible to the garbage collector. The suggested -way to do this is to make a global variable of type `Array{Ref,1}` to hold these values, until +way to do this is to make a global variable of type `Array{Ref,1}` to hold these values until the C library notifies you that it is finished with them. Whenever you have created a pointer to Julia data, you must ensure the original data exists until @@ -891,8 +839,8 @@ it must be handled in other ways. ## Non-constant Function Specifications In some cases, the exact name or path of the needed library is not known in advance and must -be computed at run time. To handle such cases, the library component of a `(name, library)` -specification can be a function call, e.g. `(:dgemm_, find_blas())`. The call expression will +be computed at run time. To handle such cases, the library component +specification can be a function call, e.g. `find_blas().dgemm`. The call expression will be executed when the `ccall` itself is executed. However, it is assumed that the library location does not change once it is determined, so the result of the call can be cached and reused. Therefore, the number of times the expression executes is unspecified, and returning @@ -901,11 +849,11 @@ different values for multiple calls results in unspecified behavior. If even more flexibility is needed, it is possible to use computed values as function names by staging through [`eval`](@ref) as follows: -``` -@eval ccall(($(string("a", "b")), "lib"), ... +```julia +@eval @ccall "lib".$(string("a", "b"))()::Cint ``` -This expression constructs a name using `string`, then substitutes this name into a new [`ccall`](@ref) +This expression constructs a name using `string`, then substitutes this name into a new `@ccall` expression, which is then evaluated. Keep in mind that `eval` only operates at the top level, so within this expression local variables will not be available (unless their values are substituted with `$`). For this reason, `eval` is typically only used to form top-level definitions, for example @@ -918,16 +866,16 @@ The next section discusses how to use indirect calls to efficiently achieve a si ## Indirect Calls -The first argument to [`ccall`](@ref) can also be an expression evaluated at run time. In this +The first argument to `@ccall` can also be an expression evaluated at run time. In this case, the expression must evaluate to a `Ptr`, which will be used as the address of the native -function to call. This behavior occurs when the first [`ccall`](@ref) argument contains references +function to call. This behavior occurs when the first `@ccall` argument contains references to non-constants, such as local variables, function arguments, or non-constant globals. For example, you might look up the function via `dlsym`, then cache it in a shared reference for that session. For example: ```julia -macro dlsym(func, lib) +macro dlsym(lib, func) z = Ref{Ptr{Cvoid}}(C_NULL) quote let zlocal = $z[] @@ -941,7 +889,7 @@ macro dlsym(func, lib) end mylibvar = Libdl.dlopen("mylib") -ccall(@dlsym("myfunc", mylibvar), Cvoid, ()) +@ccall $(@dlsym(mylibvar, "myfunc"))()::Cvoid ``` ## Closure cfunctions @@ -960,8 +908,7 @@ function qsort(a::Vector{T}, cmp) where T callback = @cfunction $cmp Cint (Ref{T}, Ref{T}) # Here, `callback` isa Base.CFunction, which will be converted to Ptr{Cvoid} # (and protected against finalization) by the ccall - ccall(:qsort, Cvoid, (Ptr{T}, Csize_t, Csize_t, Ptr{Cvoid}), - a, length(a), Base.elsize(a), callback) + @ccall qsort(a::Ptr{T}, length(a)::Csize_t, Base.elsize(a)::Csize_t, callback::Ptr{Cvoid}) # We could instead use: # GC.@preserve callback begin # use(Base.unsafe_convert(Ptr{Cvoid}, callback)) @@ -972,7 +919,7 @@ end ``` !!! note - Closure [`@cfunction`](@ref) rely on LLVM trampolines, which are not available on all + Closure [`@cfunction`](@ref) relies on LLVM trampolines, which are not available on all platforms (for example ARM and PowerPC). @@ -987,21 +934,79 @@ and load in the new changes. One can either restart Julia or use the ```julia lib = Libdl.dlopen("./my_lib.so") # Open the library explicitly. sym = Libdl.dlsym(lib, :my_fcn) # Get a symbol for the function to call. -ccall(sym, ...) # Use the pointer `sym` instead of the (symbol, library) tuple (remaining arguments are the same). +@ccall $sym(...) # Use the pointer `sym` instead of the library.symbol tuple. Libdl.dlclose(lib) # Close the library explicitly. ``` -Note that when using `ccall` with the tuple input -(e.g., `ccall((:my_fcn, "./my_lib.so"), ...)`), the library is opened implicitly +Note that when using `@ccall` with the input +(e.g., `@ccall "./my_lib.so".my_fcn(...)::Cvoid`), the library is opened implicitly and it may not be explicitly closed. -## Calling Convention +## Variadic function calls + +To call variadic C functions a `semicolon` can be used in the argument list to +separate required arguments from variadic arguments. An example with the +`printf` function is given below: + +```julia-repl +julia> @ccall printf("%s = %d\n"::Cstring ; "foo"::Cstring, foo::Cint)::Cint +foo = 3 +8 +``` + +## [`ccall` interface](@id ccall-interface) + +There is another alternative interface to `@ccall`. +This interface is slightly less convenient but it does allow one to specify a [calling convention](@ref calling-convention). + +The arguments to [`ccall`](@ref) are: + +1. A `(:function, "library")` pair (most common), + + OR + + a `:function` name symbol or `"function"` name string (for symbols in the current process or libc), + + OR + + a function pointer (for example, from `dlsym`). + +2. The function's return type + +3. A tuple of input types, corresponding to the function signature. One common mistake is forgetting that a 1-tuple of + argument types must be written with a trailing comma. + +4. The actual argument values to be passed to the function, if any; each is a separate parameter. + + +!!! note + The `(:function, "library")` pair, return type, and input types must be literal constants + (i.e., they can't be variables, but see [Non-constant Function Specifications](@ref)). + + The remaining parameters are evaluated at compile-time, when the containing method is defined. + + +A table of translations between the macro and function interfaces is given below. + +| `@ccall` | `ccall` | +|------------------------------------------------------------------------------|-----------------------------------------------------------------------------| +| `@ccall clock()::Int32` | `ccall(:clock, Int32, ())` | +| `@ccall f(a::Cint)::Cint` | `ccall(:a, Cint, (Cint,), a)` | +| `@ccall "mylib".f(a::Cint, b::Cdouble)::Cvoid` | `ccall((:f, "mylib"), Cvoid, (Cint, Cdouble), (a, b))` | +| `@ccall $fptr.f()::Cvoid` | `ccall(fptr, f, Cvoid, ())` | +| `@ccall printf("%s = %d\n"::Cstring ; "foo"::Cstring, foo::Cint)::Cint` | `` | +| `@ccall printf("%s = %d\n"::Cstring ; "2 + 2"::Cstring, "5"::Cstring)::Cint` | `ccall(:printf, Cint, (Cstring, Cstring...), "%s = %s\n", "2 + 2", "5")` | +| `` | `ccall(:gethostname, stdcall, Int32, (Ptr{UInt8}, UInt32), hn, length(hn))` | + +## [Calling Convention](@id calling-convention) -The second argument to [`ccall`](@ref) can optionally be a calling convention specifier (immediately -preceding return type). Without any specifier, the platform-default C calling convention is used. -Other supported conventions are: `stdcall`, `cdecl`, `fastcall`, and `thiscall` (no-op on 64-bit Windows). -For example (from `base/libc.jl`) we see the same `gethostname`[`ccall`](@ref) as above, but with the correct -signature for Windows: +The second argument to `ccall` (immediatel preceding return type) can optionally +be a calling convention specifier (the `@ccall` macro currently does not support +giving a calling convention). Without any specifier, the platform-default C +calling convention is used. Other supported conventions are: `stdcall`, `cdecl`, +`fastcall`, and `thiscall` (no-op on 64-bit Windows). For example (from +`base/libc.jl`) we see the same `gethostname``ccall` as above, but with the +correct signature for Windows: ```julia hn = Vector{UInt8}(undef, 256) @@ -1065,7 +1070,7 @@ the result will be a reference to this object, and the object will not be copied careful in this case to ensure that the object was always visible to the garbage collector (pointers do not count, but the new reference does) to ensure the memory is not prematurely freed. Note that if the object was not originally allocated by Julia, the new object will never be finalized -by Julia's garbage collector. If the `Ptr` itself is actually a `jl_value_t*`, it can be converted +by Julia's garbage collector. If the `Ptr` itself is actually a `jl_value_t*`, it can be converted back to a Julia object reference by [`unsafe_pointer_to_objref(ptr)`](@ref). (Julia values `v` can be converted to `jl_value_t*` pointers, as `Ptr{Cvoid}`, by calling [`pointer_from_objref(v)`](@ref).) @@ -1079,7 +1084,7 @@ a bug so that it can be resolved. If the pointer of interest is a plain-data array (primitive type or immutable struct), the function [`unsafe_wrap(Array, ptr,dims, own = false)`](@ref) may be more useful. The final parameter should be true if Julia should "take ownership" of the -underlying buffer and call `free(ptr)` when the returned `Array` object is finalized. If the +underlying buffer and call `free(ptr)` when the returned `Array` object is finalized. If the `own` parameter is omitted or false, the caller must ensure the buffer remains in existence until all access is complete. From e770f3c5d5836c666c3eadb2d1fb7aa304dc1daa Mon Sep 17 00:00:00 2001 From: ggggggggg Date: Wed, 11 May 2022 07:03:18 -0600 Subject: [PATCH 12/38] doctest for overloading show for Day(1) printing as "1 day" (#44830) --- base/multimedia.jl | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/base/multimedia.jl b/base/multimedia.jl index d15768affd012..308cc07a05a53 100644 --- a/base/multimedia.jl +++ b/base/multimedia.jl @@ -104,6 +104,18 @@ for that case. If a type benefits from custom human-readable output though, `show(::IO, ::MIME"text/plain", ::T)` should be defined. For example, the `Day` type uses `1 day` as the output for the `text/plain` MIME type, and `Day(1)` as the output of 2-argument `show`. +# Examples +```jldoctest +julia> struct Day + n::Int + end + +julia> Base.show(io::IO, ::MIME"text/plain", d::Day) = print(io, d.n, " day") + +julia> Day(1) +1 day +``` + Container types generally implement 3-argument `show` by calling `show(io, MIME"text/plain"(), x)` for elements `x`, with `:compact => true` set in an [`IOContext`](@ref) passed as the first argument. """ From 8563e73454b02d51111ac5fc00094695f66151b6 Mon Sep 17 00:00:00 2001 From: "Tamas K. Papp" Date: Wed, 11 May 2022 15:04:02 +0200 Subject: [PATCH 13/38] Add compat information in docstring of AbstractPattern. (#45264) Cf #38108. --- base/regex.jl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/base/regex.jl b/base/regex.jl index 6433eab40006d..b7e1909ece21e 100644 --- a/base/regex.jl +++ b/base/regex.jl @@ -11,6 +11,9 @@ const DEFAULT_MATCH_OPTS = PCRE.NO_UTF_CHECK An abstract type representing any sort of pattern matching expression (typically a regular expression). `AbstractPattern` objects can be used to match strings with [`match`](@ref). + +!!! compat "Julia 1.6" + This type is available in Julia 1.6 and later. """ abstract type AbstractPattern end From 5bc1d85eb2b39e41b9755e38ac49ed0b03cfb0f6 Mon Sep 17 00:00:00 2001 From: IlianPihlajamaa <73794090+IlianPihlajamaa@users.noreply.github.com> Date: Wed, 11 May 2022 15:04:26 +0200 Subject: [PATCH 14/38] Remove an unnecessary space and added clarifying information for the `rand(T, dims...)` function. --- doc/src/manual/arrays.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/src/manual/arrays.md b/doc/src/manual/arrays.md index f6e4350726269..e30564af03cdb 100644 --- a/doc/src/manual/arrays.md +++ b/doc/src/manual/arrays.md @@ -26,7 +26,7 @@ it makes avoiding unwanted copying of arrays difficult. By convention, a function name ending with a `!` indicates that it will mutate or destroy the value of one or more of its arguments (compare, for example, [`sort`](@ref) and [`sort!`](@ref)). Callees must make explicit copies to ensure that they don't modify inputs that -they don't intend to change. Many non- mutating functions are implemented by +they don't intend to change. Many non-mutating functions are implemented by calling a function of the same name with an added `!` at the end on an explicit copy of the input, and returning that copy. @@ -65,7 +65,7 @@ omitted it will default to [`Float64`](@ref). | [`deepcopy(A)`](@ref) | copy `A`, recursively copying its elements | | [`similar(A, T, dims...)`](@ref) | an uninitialized array of the same type as `A` (dense, sparse, etc.), but with the specified element type and dimensions. The second and third arguments are both optional, defaulting to the element type and dimensions of `A` if omitted. | | [`reinterpret(T, A)`](@ref) | an array with the same binary data as `A`, but with element type `T` | -| [`rand(T, dims...)`](@ref) | an `Array` with random, iid [^1] and uniformly distributed values in the half-open interval ``[0, 1)`` | +| [`rand(T, dims...)`](@ref) | an `Array` with random, iid [^1] and uniformly distributed values. For floating point types `T`, the values lie in the half-open interval ``[0, 1)``. | | [`randn(T, dims...)`](@ref) | an `Array` with random, iid and standard normally distributed values | | [`Matrix{T}(I, m, n)`](@ref) | `m`-by-`n` identity matrix. Requires `using LinearAlgebra` for [`I`](@ref). | | [`range(start, stop, n)`](@ref) | a range of `n` linearly spaced elements from `start` to `stop` | From bc0fae22124fe8b896dfaf649aa6a585120977e1 Mon Sep 17 00:00:00 2001 From: Jerry Ling Date: Wed, 11 May 2022 09:22:35 -0400 Subject: [PATCH 15/38] make `length(StepRange())` type stable (#45236) --- base/range.jl | 10 +++++----- test/ranges.jl | 5 +++++ 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/base/range.jl b/base/range.jl index e6be68be5c0fe..4fc9e7d576448 100644 --- a/base/range.jl +++ b/base/range.jl @@ -762,13 +762,13 @@ let bigints = Union{Int, UInt, Int64, UInt64, Int128, UInt128} # therefore still be valid (if the result is representable at all) # n.b. !(s isa T) if s isa Unsigned || -1 <= s <= 1 || s == -s - a = div(diff, s) + a = div(diff, s) % T elseif s < 0 - a = div(unsigned(-diff), -s) % typeof(diff) + a = div(unsigned(-diff), -s) % T else - a = div(unsigned(diff), s) % typeof(diff) + a = div(unsigned(diff), s) % T end - return Integer(a) + oneunit(a) + return a + oneunit(T) end function checked_length(r::OrdinalRange{T}) where T<:bigints s = step(r) @@ -786,7 +786,7 @@ let bigints = Union{Int, UInt, Int64, UInt64, Int128, UInt128} else a = div(checked_sub(start, stop), -s) end - return checked_add(a, oneunit(a)) + return checked_add(convert(T, a), oneunit(T)) end end diff --git a/test/ranges.jl b/test/ranges.jl index f84eaae46c321..2bf7661b55a80 100644 --- a/test/ranges.jl +++ b/test/ranges.jl @@ -2031,6 +2031,11 @@ end @test typeof(step(r)) === Int8 end +@testset "length(StepRange()) type stability" begin + typeof(length(StepRange(1,Int128(1),1))) == typeof(length(StepRange(1,Int128(1),0))) + typeof(checked_length(StepRange(1,Int128(1),1))) == typeof(checked_length(StepRange(1,Int128(1),0))) +end + @testset "LinRange eltype for element types that wrap integers" begin struct RealWrapper{T <: Real} <: Real x :: T From 1ce1fb078c77273c7b232570a92d439c8366df7e Mon Sep 17 00:00:00 2001 From: Gabriel Baraldi Date: Wed, 11 May 2022 10:23:46 -0300 Subject: [PATCH 16/38] Add alder lake to processor list (#44696) --- src/features_x86.h | 1 + src/processor_x86.cpp | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/src/features_x86.h b/src/features_x86.h index 3ef71fb217db6..93cef3d8ce30e 100644 --- a/src/features_x86.h +++ b/src/features_x86.h @@ -108,6 +108,7 @@ JL_FEATURE_DEF(clzero, 32 * 8 + 0, 0) JL_FEATURE_DEF(wbnoinvd, 32 * 8 + 9, 0) // EAX=7,ECX=1: EAX +JL_FEATURE_DEF(avxvnni, 32 * 9 + 4, 120000) JL_FEATURE_DEF(avx512bf16, 32 * 9 + 5, 0) // EAX=0x14,ECX=0: EBX diff --git a/src/processor_x86.cpp b/src/processor_x86.cpp index f18c7069fa2c2..dd9b5481837ad 100644 --- a/src/processor_x86.cpp +++ b/src/processor_x86.cpp @@ -71,6 +71,7 @@ enum class CPU : uint32_t { intel_corei7_icelake_client, intel_corei7_icelake_server, intel_corei7_tigerlake, + intel_corei7_alderlake, intel_corei7_sapphirerapids, intel_knights_landing, intel_knights_mill, @@ -136,6 +137,7 @@ static constexpr FeatureDep deps[] = { {vaes, aes}, {vpclmulqdq, avx}, {vpclmulqdq, pclmul}, + {avxvnni, avx2}, {avx512f, avx2}, {avx512dq, avx512f}, {avx512ifma, avx512f}, @@ -202,6 +204,8 @@ constexpr auto icelake = cannonlake | get_feature_masks(avx512bitalg, vaes, avx5 constexpr auto icelake_server = icelake | get_feature_masks(pconfig, wbnoinvd); constexpr auto tigerlake = icelake | get_feature_masks(avx512vp2intersect, movdiri, movdir64b, shstk); +constexpr auto alderlake = skylake | get_feature_masks(clwb, sha, waitpkg, shstk, gfni, vaes, vpclmulqdq, pconfig, + rdpid, movdiri, pku, movdir64b, serialize, ptwrite, avxvnni); constexpr auto sapphirerapids = icelake_server | get_feature_masks(amx_tile, amx_int8, amx_bf16, avx512bf16, serialize, cldemote, waitpkg, ptwrite, tsxldtrk, enqcmd, shstk, avx512vp2intersect, movdiri, movdir64b); @@ -255,6 +259,8 @@ static constexpr CPUSpec cpus[] = { Feature::icelake_server}, {"tigerlake", CPU::intel_corei7_tigerlake, CPU::intel_corei7_icelake_client, 100000, Feature::tigerlake}, + {"alderlake", CPU::intel_corei7_alderlake, CPU::intel_corei7_skylake, 120000, + Feature::alderlake}, {"sapphirerapids", CPU::intel_corei7_sapphirerapids, CPU::intel_corei7_icelake_server, 120000, Feature::sapphirerapids}, @@ -411,6 +417,10 @@ static CPU get_intel_processor_name(uint32_t family, uint32_t model, uint32_t br case 0x8c: case 0x8d: return CPU::intel_corei7_tigerlake; + //Alder Lake + case 0x97: + case 0x9a: + return CPU::intel_corei7_alderlake; // Sapphire Rapids case 0x8f: From b04d116c7c9064f0e8aa91b926fffa915265ae6c Mon Sep 17 00:00:00 2001 From: Jim Garrison Date: Wed, 11 May 2022 09:33:12 -0400 Subject: [PATCH 17/38] Add section on additional spurious warnings to the Valgrind devdocs (#43559) --- doc/src/devdocs/valgrind.md | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/doc/src/devdocs/valgrind.md b/doc/src/devdocs/valgrind.md index 8a11cb411a6fd..7e62aeb176f3c 100644 --- a/doc/src/devdocs/valgrind.md +++ b/doc/src/devdocs/valgrind.md @@ -16,7 +16,7 @@ memory pools disabled. The compile-time flag `MEMDEBUG` disables memory pools i `MEMDEBUG2` disables memory pools in FemtoLisp. To build `julia` with both flags, add the following line to `Make.user`: -```julia +```make CFLAGS = -DMEMDEBUG -DMEMDEBUG2 ``` @@ -55,6 +55,32 @@ valgrind --smc-check=all-non-file --trace-children=yes --suppressions=$PWD/../co If you would like to see a report of "definite" memory leaks, pass the flags `--leak-check=full --show-leak-kinds=definite` to `valgrind` as well. +## Additional spurious warnings + +This section covers Valgrind warnings which cannot be added to the +suppressions file yet are nonetheless safe to ignore. + +### Unhandled rr system calls + +Valgrind will emit a warning if it encounters any of the [system calls +that are specific to +rr](https://github.com/rr-debugger/rr/blob/master/src/preload/rrcalls.h), +the [Record and Replay Framework](https://rr-project.org/). In +particular, a warning about an unhandled `1008` syscall will be shown +when julia tries to detect whether it is running under rr: + +``` +--xxxxxx-- WARNING: unhandled amd64-linux syscall: 1008 +--xxxxxx-- You may be able to write your own handler. +--xxxxxx-- Read the file README_MISSING_SYSCALL_OR_IOCTL. +--xxxxxx-- Nevertheless we consider this a bug. Please report +--xxxxxx-- it at http://valgrind.org/support/bug_reports.html. +``` + +This issue +[has been reported](https://bugs.kde.org/show_bug.cgi?id=446401) +to the Valgrind developers as they have requested. + ## Caveats Valgrind currently [does not support multiple rounding modes](https://bugs.kde.org/show_bug.cgi?id=136779), From 4adf56b68f06fab60447534402f36b4f0bc86b20 Mon Sep 17 00:00:00 2001 From: John Gardner <79354642+git-john-gardner@users.noreply.github.com> Date: Wed, 11 May 2022 14:43:10 +0100 Subject: [PATCH 18/38] add test for trying to create BitArray from infinite iter (#42067) --- test/bitarray.jl | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/bitarray.jl b/test/bitarray.jl index 9ce3775a5d409..d17a9856596a4 100644 --- a/test/bitarray.jl +++ b/test/bitarray.jl @@ -217,6 +217,11 @@ timesofar("utils") @test_throws DimensionMismatch BitMatrix((isodd(i) for i in 1:3)) end + @testset "constructor from infinite iterator" begin + inf_iter = Base.Iterators.cycle([true]) + @test_throws ArgumentError BitArray(inf_iter) + end + @testset "constructor from NTuple" begin for nt in ((true, false, false), NTuple{0,Bool}(), (false,), (true,)) @test BitVector(nt) == BitVector(collect(nt)) From fbf83efdc0314bd1aa26782bfac0638556713bc6 Mon Sep 17 00:00:00 2001 From: Eric Davies Date: Wed, 11 May 2022 08:47:18 -0500 Subject: [PATCH 19/38] Speed up vect by delegating to getindex (#41696) --- base/array.jl | 4 +--- test/compiler/codegen.jl | 8 +++++++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/base/array.jl b/base/array.jl index b5a1ba31f0acf..c1b8e4cc1f07f 100644 --- a/base/array.jl +++ b/base/array.jl @@ -142,9 +142,7 @@ julia> a = Base.vect(UInt8(1), 2.5, 1//2) """ function vect(X...) T = promote_typeof(X...) - #T[ X[i] for i=1:length(X) ] - # TODO: this is currently much faster. should figure out why. not clear. - return copyto!(Vector{T}(undef, length(X)), X) + return T[X...] end size(a::Array, d::Integer) = arraysize(a, convert(Int, d)) diff --git a/test/compiler/codegen.jl b/test/compiler/codegen.jl index 9724091637f97..bf52d1705bd00 100644 --- a/test/compiler/codegen.jl +++ b/test/compiler/codegen.jl @@ -418,9 +418,15 @@ let src = get_llvm(f33829, Tuple{Float64}, true, true) @test !occursin(r"call [^(]*\{}", src) end +# Base.vect prior to PR 41696 +function oldvect(X...) + T = Base.promote_typeof(X...) + return copyto!(Vector{T}(undef, length(X)), X) +end + let io = IOBuffer() # Test for the f(args...) = g(args...) generic codegen optimization - code_llvm(io, Base.vect, Tuple{Vararg{Union{Float64, Int64}}}) + code_llvm(io, oldvect, Tuple{Vararg{Union{Float64, Int64}}}) @test !occursin("__apply", String(take!(io))) end From 70741845f7fda9e71fe1111b1bd8f7a515bc3ca1 Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Wed, 11 May 2022 15:47:37 +0200 Subject: [PATCH 20/38] Revert "add a test to ensure conj! is not called on uninitialized memory in matmul (#40491)" (#45278) This reverts commit 5e83ff52be92b602e1b89119b224e5373fbdd182. --- stdlib/LinearAlgebra/test/matmul.jl | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/stdlib/LinearAlgebra/test/matmul.jl b/stdlib/LinearAlgebra/test/matmul.jl index 20744f104936e..cf0295ce552b5 100644 --- a/stdlib/LinearAlgebra/test/matmul.jl +++ b/stdlib/LinearAlgebra/test/matmul.jl @@ -825,17 +825,6 @@ end @test Matrix{Int}(undef, 2, 0) * Matrix{Int}(undef, 0, 3) == zeros(Int, 2, 3) end -struct BrokenInt <: Number - i::Int -end -Base.:*(::BrokenInt, ::BrokenInt) = BrokenInt(42) -Base.:+(::BrokenInt, ::BrokenInt) = BrokenInt(42) -Base.zero(::BrokenInt) = BrokenInt(0) -Base.conj(b::BrokenInt) = b.i == 42 ? b : error() -@testset "matmul uninit memory #40481" begin - @test fill(BrokenInt(42), 10,100)' * fill(BrokenInt(42), 100,10)' == fill(BrokenInt(42), 100, 100) -end - @testset "3-arg *, order by type" begin x = [1, 2im] y = [im, 20, 30 + 40im] From 13ae0790ababe087a0ac2eab6f8d0edb99d4cb31 Mon Sep 17 00:00:00 2001 From: Michael Abbott <32575566+mcabbott@users.noreply.github.com> Date: Wed, 11 May 2022 09:58:10 -0400 Subject: [PATCH 21/38] Fast paths for `allunique` (#43375) --- base/set.jl | 50 ++++++++++++++++++++++++++++++++++++++++++-------- test/sets.jl | 20 ++++++++++++++++++++ 2 files changed, 62 insertions(+), 8 deletions(-) diff --git a/base/set.jl b/base/set.jl index 66b5ef33fb4f3..b77d39e80ae5d 100644 --- a/base/set.jl +++ b/base/set.jl @@ -384,20 +384,28 @@ See also: [`unique`](@ref), [`issorted`](@ref), [`allequal`](@ref). # Examples ```jldoctest -julia> a = [1; 2; 3] -3-element Vector{Int64}: - 1 - 2 - 3 - -julia> allunique(a) +julia> allunique([1, 2, 3]) true -julia> allunique([a, a]) +julia> allunique([1, 2, 1, 2]) +false + +julia> allunique(Real[1, 1.0, 2]) +false + +julia> allunique([NaN, 2.0, NaN, 4.0]) false ``` """ function allunique(C) + if haslength(C) + length(C) < 2 && return true + length(C) < 32 && return _indexed_allunique(collect(C)) + end + return _hashed_allunique(C) +end + +function _hashed_allunique(C) seen = Set{eltype(C)}() x = iterate(C) if haslength(C) && length(C) > 1000 @@ -420,6 +428,32 @@ allunique(::Union{AbstractSet,AbstractDict}) = true allunique(r::AbstractRange) = !iszero(step(r)) || length(r) <= 1 +allunique(A::StridedArray) = length(A) < 32 ? _indexed_allunique(A) : _hashed_allunique(A) + +function _indexed_allunique(A) + length(A) < 2 && return true + iter = eachindex(A) + I = iterate(iter) + while I !== nothing + i, s = I + a = A[i] + for j in Iterators.rest(iter, s) + isequal(a, @inbounds A[j]) && return false + end + I = iterate(iter, s) + end + return true +end + +function allunique(t::Tuple) + length(t) < 32 || return _hashed_allunique(t) + a = afoldl(true, tail(t)...) do b, x + b & !isequal(first(t), x) + end + return a && allunique(tail(t)) +end +allunique(t::Tuple{}) = true + """ allequal(itr) -> Bool diff --git a/test/sets.jl b/test/sets.jl index b16ced60b8aaa..9410739596486 100644 --- a/test/sets.jl +++ b/test/sets.jl @@ -499,10 +499,23 @@ end @test allunique([]) @test allunique(Set()) @test allunique([1,2,3]) + @test allunique([1 2; 3 4]) @test allunique([:a,:b,:c]) @test allunique(Set([1,2,3])) @test !allunique([1,1,2]) @test !allunique([:a,:b,:c,:a]) + @test allunique(unique(randn(100))) # longer than 32 + @test allunique(collect('A':'z')) # 58-element Vector{Char} + @test !allunique(repeat(1:99, 1, 2)) + @test !allunique(vcat(pi, randn(1998), pi)) # longer than 1000 + @test allunique(eachrow(hcat(1:10, 1:10))) + @test allunique(x for x in 'A':'Z' if randn()>0) + @test !allunique(x for x in repeat(1:2000, 3) if true) + @test allunique([0.0, -0.0]) + @test allunique(x for x in [0.0, -0.0] if true) + @test !allunique([NaN, NaN]) + @test !allunique(x for x in [NaN, NaN] if true) + # ranges @test allunique(4:7) @test allunique(1:1) @test allunique(4.0:0.3:7.0) @@ -519,6 +532,13 @@ end LinRange(1, 2, 3), LinRange(1, 1, 0), LinRange(1, 1, 1), LinRange(1, 1, 10)) @test allunique(r) == invoke(allunique, Tuple{Any}, r) end + # tuples + @test allunique(()) + @test allunique((1,2,3)) + @test allunique(ntuple(identity, 40)) + @test !allunique((1,2,3,4,3)) + @test allunique((0.0, -0.0)) + @test !allunique((NaN, NaN)) end @testset "allequal" begin From 72b80e2b158b9d4016e70791efdc81fead933881 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Wed, 11 May 2022 11:23:59 -0400 Subject: [PATCH 22/38] codegen: add handling for undefined phinode values (#45155) The optimization pass often uses values for phi values (and thus by extension, also for pi, phic and upsilon values) that are invalid. We make sure that these have a null pointer, so that we can detect that case at runtime (at the cost of slightly worse code generation for them), but it means we need to be very careful to check for that. This is identical to #39747, which added the equivalent code to the other side of the conditional there, but missed some additional relevant, but rare, cases that are observed to be possible. The `emit_isa_and_defined` is derived from the LLVM name for this operation: `isa_and_nonnull`. Secondly, we also optimize `emit_unionmove` to change a bad IR case to a better IR form. Fix #44501 --- src/cgutils.cpp | 120 ++++++++++++++++++---------------- src/codegen.cpp | 30 +++++---- src/llvm-late-gc-lowering.cpp | 3 +- 3 files changed, 83 insertions(+), 70 deletions(-) diff --git a/src/cgutils.cpp b/src/cgutils.cpp index 6f346b32728b3..655fe33eff294 100644 --- a/src/cgutils.cpp +++ b/src/cgutils.cpp @@ -847,7 +847,7 @@ static bool is_uniontype_allunboxed(jl_value_t *typ) return for_each_uniontype_small([&](unsigned, jl_datatype_t*) {}, typ, counter); } -static Value *emit_typeof_boxed(jl_codectx_t &ctx, const jl_cgval_t &p); +static Value *emit_typeof_boxed(jl_codectx_t &ctx, const jl_cgval_t &p, bool maybenull=false); static unsigned get_box_tindex(jl_datatype_t *jt, jl_value_t *ut) { @@ -902,16 +902,9 @@ static LoadInst *emit_nthptr_recast(jl_codectx_t &ctx, Value *v, ssize_t n, MDNo } static Value *boxed(jl_codectx_t &ctx, const jl_cgval_t &v); +static Value *emit_typeof(jl_codectx_t &ctx, Value *v, bool maybenull); -// Returns ctx.types().T_prjlvalue -static Value *emit_typeof(jl_codectx_t &ctx, Value *tt) -{ - ++EmittedTypeof; - assert(tt != NULL && !isa(tt) && "expected a conditionally boxed value"); - return ctx.builder.CreateCall(prepare_call(jl_typeof_func), {tt}); -} - -static jl_cgval_t emit_typeof(jl_codectx_t &ctx, const jl_cgval_t &p) +static jl_cgval_t emit_typeof(jl_codectx_t &ctx, const jl_cgval_t &p, bool maybenull) { // given p, compute its type if (p.constant) @@ -924,7 +917,7 @@ static jl_cgval_t emit_typeof(jl_codectx_t &ctx, const jl_cgval_t &p) return mark_julia_const(ctx, jl_typeof(tp)); } } - return mark_julia_type(ctx, emit_typeof(ctx, p.V), true, jl_datatype_type); + return mark_julia_type(ctx, emit_typeof(ctx, p.V, maybenull), true, jl_datatype_type); } if (p.TIndex) { Value *tindex = ctx.builder.CreateAnd(p.TIndex, ConstantInt::get(getInt8Ty(ctx.builder.getContext()), 0x7f)); @@ -959,7 +952,7 @@ static jl_cgval_t emit_typeof(jl_codectx_t &ctx, const jl_cgval_t &p) BasicBlock *mergeBB = BasicBlock::Create(ctx.builder.getContext(), "merge", ctx.f); ctx.builder.CreateCondBr(isnull, boxBB, unboxBB); ctx.builder.SetInsertPoint(boxBB); - auto boxTy = emit_typeof(ctx, p.Vboxed); + auto boxTy = emit_typeof(ctx, p.Vboxed, maybenull); ctx.builder.CreateBr(mergeBB); boxBB = ctx.builder.GetInsertBlock(); // could have changed ctx.builder.SetInsertPoint(unboxBB); @@ -981,9 +974,9 @@ static jl_cgval_t emit_typeof(jl_codectx_t &ctx, const jl_cgval_t &p) } // Returns ctx.types().T_prjlvalue -static Value *emit_typeof_boxed(jl_codectx_t &ctx, const jl_cgval_t &p) +static Value *emit_typeof_boxed(jl_codectx_t &ctx, const jl_cgval_t &p, bool maybenull) { - return boxed(ctx, emit_typeof(ctx, p)); + return boxed(ctx, emit_typeof(ctx, p, maybenull)); } static Value *emit_datatype_types(jl_codectx_t &ctx, Value *dt) @@ -1224,6 +1217,24 @@ static Value *emit_nullcheck_guard2(jl_codectx_t &ctx, Value *nullcheck1, }); } +// Returns typeof(v), or null if v is a null pointer at run time and maybenull is true. +// This is used when the value might have come from an undefined value (a PhiNode), +// yet we try to read its type to compute a union index when moving the value (a PiNode). +// Returns a ctx.types().T_prjlvalue typed Value +static Value *emit_typeof(jl_codectx_t &ctx, Value *v, bool maybenull) +{ + ++EmittedTypeof; + assert(v != NULL && !isa(v) && "expected a conditionally boxed value"); + Function *typeof = prepare_call(jl_typeof_func); + if (maybenull) + return emit_guarded_test(ctx, null_pointer_cmp(ctx, v), Constant::getNullValue(typeof->getReturnType()), [&] { + // e.g. emit_typeof(ctx, v) + return ctx.builder.CreateCall(typeof, {v}); + }); + return ctx.builder.CreateCall(typeof, {v}); +} + + static void emit_type_error(jl_codectx_t &ctx, const jl_cgval_t &x, Value *type, const std::string &msg) { Value *msg_val = stringConstPtr(ctx.emission_context, ctx.builder, msg); @@ -1353,7 +1364,7 @@ static std::pair emit_isa(jl_codectx_t &ctx, const jl_cgval_t &x, BasicBlock *postBB = BasicBlock::Create(ctx.builder.getContext(), "post_isa", ctx.f); ctx.builder.CreateCondBr(isboxed, isaBB, postBB); ctx.builder.SetInsertPoint(isaBB); - Value *istype_boxed = ctx.builder.CreateICmpEQ(emit_typeof(ctx, x.Vboxed), + Value *istype_boxed = ctx.builder.CreateICmpEQ(emit_typeof(ctx, x.Vboxed, false), track_pjlvalue(ctx, literal_pointer_val(ctx, intersected_type))); ctx.builder.CreateBr(postBB); isaBB = ctx.builder.GetInsertBlock(); // could have changed @@ -1409,6 +1420,20 @@ static std::pair emit_isa(jl_codectx_t &ctx, const jl_cgval_t &x, ConstantInt::get(getInt32Ty(ctx.builder.getContext()), 0)), false); } +// If this might have been sourced from a PhiNode object, it is possible our +// Vboxed pointer itself is null (undef) at runtime even if we thought we should +// know exactly the type of the bytes that should have been inside. +// +// n.b. It is also possible the value is a ghost of some sort, and we will +// declare that the pointer is legal (for zero bytes) even though it might be undef. +static Value *emit_isa_and_defined(jl_codectx_t &ctx, const jl_cgval_t &val, jl_value_t *typ) +{ + return emit_nullcheck_guard(ctx, val.ispointer() ? val.V : nullptr, [&] { + return emit_isa(ctx, val, typ, nullptr).first; + }); +} + + static void emit_typecheck(jl_codectx_t &ctx, const jl_cgval_t &x, jl_value_t *type, const std::string &msg) { Value *istype; @@ -3005,42 +3030,16 @@ static Value *compute_box_tindex(jl_codectx_t &ctx, Value *datatype, jl_value_t return tindex; } -// Returns typeof(v), or null if v is a null pointer at run time. -// This is used when the value might have come from an undefined variable, -// yet we try to read its type to compute a union index when moving the value. -static Value *emit_typeof_or_null(jl_codectx_t &ctx, Value *v) -{ - BasicBlock *nonnull = BasicBlock::Create(ctx.builder.getContext(), "nonnull", ctx.f); - BasicBlock *postBB = BasicBlock::Create(ctx.builder.getContext(), "postnull", ctx.f); - Value *isnull = ctx.builder.CreateICmpEQ(v, Constant::getNullValue(v->getType())); - ctx.builder.CreateCondBr(isnull, postBB, nonnull); - BasicBlock *entry = ctx.builder.GetInsertBlock(); - ctx.builder.SetInsertPoint(nonnull); - Value *typof = emit_typeof(ctx, v); - ctx.builder.CreateBr(postBB); - nonnull = ctx.builder.GetInsertBlock(); // could have changed - ctx.builder.SetInsertPoint(postBB); - PHINode *ti = ctx.builder.CreatePHI(typof->getType(), 2); - ti->addIncoming(Constant::getNullValue(typof->getType()), entry); - ti->addIncoming(typof, nonnull); - return ti; -} - // get the runtime tindex value, assuming val is already converted to type typ if it has a TIndex -static Value *compute_tindex_unboxed(jl_codectx_t &ctx, const jl_cgval_t &val, jl_value_t *typ) +static Value *compute_tindex_unboxed(jl_codectx_t &ctx, const jl_cgval_t &val, jl_value_t *typ, bool maybenull=false) { if (val.typ == jl_bottom_type) return UndefValue::get(getInt8Ty(ctx.builder.getContext())); if (val.constant) return ConstantInt::get(getInt8Ty(ctx.builder.getContext()), get_box_tindex((jl_datatype_t*)jl_typeof(val.constant), typ)); - if (val.TIndex) return ctx.builder.CreateAnd(val.TIndex, ConstantInt::get(getInt8Ty(ctx.builder.getContext()), 0x7f)); - Value *typof; - if (val.isboxed && !jl_is_concrete_type(val.typ) && !jl_is_type_type(val.typ)) - typof = emit_typeof_or_null(ctx, val.V); - else - typof = emit_typeof_boxed(ctx, val); + Value *typof = emit_typeof_boxed(ctx, val, maybenull); return compute_box_tindex(ctx, typof, val.typ, typ); } @@ -3222,14 +3221,17 @@ static void emit_unionmove(jl_codectx_t &ctx, Value *dest, MDNode *tbaa_dst, con Value *src_ptr = data_pointer(ctx, src); unsigned nb = jl_datatype_size(typ); unsigned alignment = julia_alignment(typ); - Value *nbytes = ConstantInt::get(getSizeTy(ctx.builder.getContext()), nb); - if (skip) { - // TODO: this Select is very bad for performance, but is necessary to work around LLVM bugs with the undef option that we want to use: - // select copy dest -> dest to simulate an undef value / conditional copy - // src_ptr = ctx.builder.CreateSelect(skip, dest, src_ptr); - nbytes = ctx.builder.CreateSelect(skip, Constant::getNullValue(getSizeTy(ctx.builder.getContext())), nbytes); - } - emit_memcpy(ctx, dest, tbaa_dst, src_ptr, src.tbaa, nbytes, alignment, isVolatile); + // TODO: this branch may be bad for performance, but is necessary to work around LLVM bugs with the undef option that we want to use: + // select copy dest -> dest to simulate an undef value / conditional copy + // if (skip) src_ptr = ctx.builder.CreateSelect(skip, dest, src_ptr); + auto f = [&] { + (void)emit_memcpy(ctx, dest, tbaa_dst, src_ptr, src.tbaa, nb, alignment, isVolatile); + return nullptr; + }; + if (skip) + emit_guarded_test(ctx, skip, nullptr, f); + else + f(); } } } @@ -3282,12 +3284,16 @@ static void emit_unionmove(jl_codectx_t &ctx, Value *dest, MDNode *tbaa_dst, con } else { assert(src.isboxed && "expected boxed value for sizeof/alignment computation"); - Value *datatype = emit_typeof_boxed(ctx, src); - Value *copy_bytes = emit_datatype_size(ctx, datatype); - if (skip) { - copy_bytes = ctx.builder.CreateSelect(skip, ConstantInt::get(copy_bytes->getType(), 0), copy_bytes); - } - emit_memcpy(ctx, dest, tbaa_dst, src, copy_bytes, /*TODO: min-align*/1, isVolatile); + auto f = [&] { + Value *datatype = emit_typeof_boxed(ctx, src); + Value *copy_bytes = emit_datatype_size(ctx, datatype); + emit_memcpy(ctx, dest, tbaa_dst, src, copy_bytes, /*TODO: min-align*/1, isVolatile); + return nullptr; + }; + if (skip) + emit_guarded_test(ctx, skip, nullptr, f); + else + f(); } } diff --git a/src/codegen.cpp b/src/codegen.cpp index fa7485a8448af..a95631962e93d 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -1816,7 +1816,7 @@ static jl_cgval_t convert_julia_type_union(jl_codectx_t &ctx, const jl_cgval_t & if (!union_isaBB) { union_isaBB = BasicBlock::Create(ctx.builder.getContext(), "union_isa", ctx.f); ctx.builder.SetInsertPoint(union_isaBB); - union_box_dt = emit_typeof_or_null(ctx, v.Vboxed); + union_box_dt = emit_typeof(ctx, v.Vboxed, skip != NULL); post_union_isaBB = ctx.builder.GetInsertBlock(); } }; @@ -1915,7 +1915,6 @@ static jl_cgval_t convert_julia_type(jl_codectx_t &ctx, const jl_cgval_t &v, jl_ return ghostValue(ctx, typ); Value *new_tindex = NULL; if (jl_is_concrete_type(typ)) { - assert(skip == nullptr && "skip only valid for union type return"); if (v.TIndex && !jl_is_pointerfree(typ)) { // discovered that this union-split type must actually be isboxed if (v.Vboxed) { @@ -1923,14 +1922,20 @@ static jl_cgval_t convert_julia_type(jl_codectx_t &ctx, const jl_cgval_t &v, jl_ } else { // type mismatch: there weren't any boxed values in the union - CreateTrap(ctx.builder); + if (skip) + *skip = ConstantInt::get(getInt1Ty(ctx.builder.getContext()), 1); + else + CreateTrap(ctx.builder); return jl_cgval_t(ctx.builder.getContext()); } } if (jl_is_concrete_type(v.typ) && !jl_is_kind(v.typ)) { if (jl_is_concrete_type(typ) && !jl_is_kind(typ)) { // type mismatch: changing from one leaftype to another - CreateTrap(ctx.builder); + if (skip) + *skip = ConstantInt::get(getInt1Ty(ctx.builder.getContext()), 1); + else + CreateTrap(ctx.builder); return jl_cgval_t(ctx.builder.getContext()); } } @@ -2938,7 +2943,7 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, } else if (f == jl_builtin_typeof && nargs == 1) { - *ret = emit_typeof(ctx, argv[1]); + *ret = emit_typeof(ctx, argv[1], false); return true; } @@ -4129,7 +4134,7 @@ static jl_cgval_t emit_sparam(jl_codectx_t &ctx, size_t i) ctx.spvals_ptr, i + sizeof(jl_svec_t) / sizeof(jl_value_t*)); Value *sp = tbaa_decorate(ctx.tbaa().tbaa_const, ctx.builder.CreateAlignedLoad(ctx.types().T_prjlvalue, bp, Align(sizeof(void*)))); - Value *isnull = ctx.builder.CreateICmpNE(emit_typeof(ctx, sp), + Value *isnull = ctx.builder.CreateICmpNE(emit_typeof(ctx, sp, false), track_pjlvalue(ctx, literal_pointer_val(ctx, (jl_value_t*)jl_tvar_type))); jl_unionall_t *sparam = (jl_unionall_t*)ctx.linfo->def.method->sig; for (size_t j = 0; j < i; j++) { @@ -4204,7 +4209,7 @@ static jl_cgval_t emit_isdefined(jl_codectx_t &ctx, jl_value_t *sym) ctx.spvals_ptr, i + sizeof(jl_svec_t) / sizeof(jl_value_t*)); Value *sp = tbaa_decorate(ctx.tbaa().tbaa_const, ctx.builder.CreateAlignedLoad(ctx.types().T_prjlvalue, bp, Align(sizeof(void*)))); - isnull = ctx.builder.CreateICmpNE(emit_typeof(ctx, sp), + isnull = ctx.builder.CreateICmpNE(emit_typeof(ctx, sp, false), track_pjlvalue(ctx, literal_pointer_val(ctx, (jl_value_t*)jl_tvar_type))); } else { @@ -4378,7 +4383,7 @@ static void emit_vi_assignment_unboxed(jl_codectx_t &ctx, jl_varinfo_t &vi, Valu } } else { - emit_unionmove(ctx, vi.value.V, ctx.tbaa().tbaa_stack, rval_info, isboxed, vi.isVolatile); + emit_unionmove(ctx, vi.value.V, ctx.tbaa().tbaa_stack, rval_info, /*skip*/isboxed, vi.isVolatile); } } } @@ -4840,7 +4845,8 @@ static jl_cgval_t emit_expr(jl_codectx_t &ctx, jl_value_t *expr, ssize_t ssaval) jl_error("GotoIfNot in value position"); } if (jl_is_pinode(expr)) { - return convert_julia_type(ctx, emit_expr(ctx, jl_fieldref_noalloc(expr, 0)), jl_fieldref_noalloc(expr, 1)); + Value *skip = NULL; + return convert_julia_type(ctx, emit_expr(ctx, jl_fieldref_noalloc(expr, 0)), jl_fieldref_noalloc(expr, 1), &skip); } if (!jl_is_expr(expr)) { int needroot = true; @@ -7717,7 +7723,7 @@ static jl_llvm_functions_t else { // must be careful to emit undef here (rather than a bitcast or // load of val) if the runtime type of val isn't phiType - Value *isvalid = emit_isa(ctx, val, phiType, NULL).first; + Value *isvalid = emit_isa_and_defined(ctx, val, phiType); V = emit_guarded_test(ctx, isvalid, undef_value_for_type(VN->getType()), [&] { return emit_unbox(ctx, VN->getType(), val, phiType); }); @@ -7729,7 +7735,7 @@ static jl_llvm_functions_t // must be careful to emit undef here (rather than a bitcast or // load of val) if the runtime type of val isn't phiType assert(lty != ctx.types().T_prjlvalue); - Value *isvalid = emit_isa(ctx, val, phiType, NULL).first; + Value *isvalid = emit_isa_and_defined(ctx, val, phiType); emit_guarded_test(ctx, isvalid, nullptr, [&] { (void)emit_unbox(ctx, lty, val, phiType, maybe_decay_tracked(ctx, dest), ctx.tbaa().tbaa_stack); return nullptr; @@ -7771,7 +7777,7 @@ static jl_llvm_functions_t RTindex = new_union.TIndex; if (!RTindex) { assert(new_union.isboxed && new_union.Vboxed && "convert_julia_type failed"); - RTindex = compute_tindex_unboxed(ctx, new_union, phiType); + RTindex = compute_tindex_unboxed(ctx, new_union, phiType, true); if (dest) { // If dest is not set, this is a ghost union, the recipient of which // is often not prepared to handle a boxed representation of the ghost. diff --git a/src/llvm-late-gc-lowering.cpp b/src/llvm-late-gc-lowering.cpp index cf4d771f02a89..335d0803b8638 100644 --- a/src/llvm-late-gc-lowering.cpp +++ b/src/llvm-late-gc-lowering.cpp @@ -1071,8 +1071,9 @@ void RecursivelyVisit(callback f, Value *V) { if (isa(TheUser)) f(VU); if (isa(TheUser) || isa(TheUser) || - isa(TheUser) || isa(TheUser) || + isa(TheUser) || isa(TheUser) || // TODO: should these be removed from this list? isa(TheUser) || isa(TheUser) || + isa(TheUser) || // ICmpEQ/ICmpNE can be used with ptr types isa(TheUser) || isa(TheUser)) continue; if (isa(TheUser) || isa(TheUser) || isa(TheUser)) { From c81c25a54296ee3d23d7215da103f4e1a48afe10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stephan=20Kleinb=C3=B6lting?= Date: Wed, 11 May 2022 19:52:15 +0200 Subject: [PATCH 23/38] Docstring for `outer` keyword (#45231) --- base/docs/basedocs.jl | 51 ++++++++++++++++++++++++++++++++++ doc/src/base/base.md | 3 +- doc/src/manual/control-flow.md | 48 +++++++++++++++++++------------- 3 files changed, 81 insertions(+), 21 deletions(-) diff --git a/base/docs/basedocs.jl b/base/docs/basedocs.jl index 777af24c779ec..c8f5da022e78b 100644 --- a/base/docs/basedocs.jl +++ b/base/docs/basedocs.jl @@ -279,6 +279,53 @@ julia> z """ kw"global" +""" + for outer + +Reuse an existing local variable for iteration in a `for` loop. + +See the [manual section on variable scoping](@ref scope-of-variables) for more information. + +See also [`for`](@ref). + + +# Examples +```jldoctest +julia> function f() + i = 0 + for i = 1:3 + # empty + end + return i + end; + +julia> f() +0 +``` + +```jldoctest +julia> function f() + i = 0 + for outer i = 1:3 + # empty + end + return i + end; + +julia> f() +3 +``` + +```jldoctest +julia> i = 0 # global variable + for outer i = 1:3 + end +ERROR: syntax: no outer local variable declaration exists for "for outer" +[...] +``` +""" +kw"outer" + """ ' ' @@ -834,6 +881,10 @@ kw"?", kw"?:" `for` loops repeatedly evaluate a block of statements while iterating over a sequence of values. +The iteration variable is always a new variable, even if a variable of the same name +exists in the enclosing scope. +Use [`outer`](@ref) to reuse an existing local variable for iteration. + # Examples ```jldoctest julia> for i in [1, 4, 0] diff --git a/doc/src/base/base.md b/doc/src/base/base.md index dd6e51518acf3..b9e45d2b291a8 100644 --- a/doc/src/base/base.md +++ b/doc/src/base/base.md @@ -59,7 +59,7 @@ However, you can create variables with names: Finally: `where` is parsed as an infix operator for writing parametric method and type definitions; `in` and `isa` are parsed as infix operators; -and `outer` is parsed as a keyword when used to modify the scope of a variable in an iteration specification of a `for` loop or `generator` expression. +and `outer` is parsed as a keyword when used to modify the scope of a variable in an iteration specification of a `for` loop. Creation of variables named `where`, `in`, `isa` or `outer` is allowed though. ```@docs @@ -85,6 +85,7 @@ finally quote local global +outer const struct mutable struct diff --git a/doc/src/manual/control-flow.md b/doc/src/manual/control-flow.md index 63832cc4c90c9..92c927f9aa2da 100644 --- a/doc/src/manual/control-flow.md +++ b/doc/src/manual/control-flow.md @@ -388,15 +388,13 @@ loop. Here is an example of a `while` loop: ```jldoctest julia> i = 1; -julia> while i <= 5 +julia> while i <= 3 println(i) global i += 1 end 1 2 3 -4 -5 ``` The `while` loop evaluates the condition expression (`i <= 5` in this case), and as long it remains @@ -408,39 +406,53 @@ down like the above `while` loop does is so common, it can be expressed more con `for` loop: ```jldoctest -julia> for i = 1:5 +julia> for i = 1:3 println(i) end 1 2 3 -4 -5 ``` -Here the `1:5` is a range object, representing the sequence of numbers 1, 2, 3, 4, 5. The `for` +Here the `1:3` is a range object, representing the sequence of numbers 1, 2, 3. The `for` loop iterates through these values, assigning each one in turn to the variable `i`. One rather important distinction between the previous `while` loop form and the `for` loop form is the scope -during which the variable is visible. If the variable `i` has not been introduced in another -scope, in the `for` loop form, it is visible only inside of the `for` loop, and not -outside/afterwards. You'll either need a new interactive session instance or a different variable +during which the variable is visible. A `for` loop always introduces a new iteration variable in +its body, regardless of whether a variable of the same name exists in the enclosing scope. +This implies that on the one hand `i` need not be declared before the loop. On the other hand it +will not be visible outside the loop, nor will an outside variable of the same name be affected. +You'll either need a new interactive session instance or a different variable name to test this: ```jldoctest -julia> for j = 1:5 +julia> for j = 1:3 println(j) end 1 2 3 -4 -5 julia> j ERROR: UndefVarError: j not defined ``` -See [Scope of Variables](@ref scope-of-variables) for a detailed explanation of variable scope and how it works in +```jldoctest +julia> j = 0; + +julia> for j = 1:3 + println(j) + end +1 +2 +3 + +julia> j +0 +``` + +Use `for outer` to modify the latter behavior and reuse an existing local variable. + +See [Scope of Variables](@ref scope-of-variables) for a detailed explanation of variable scope, [`outer`](@ref), and how it works in Julia. In general, the `for` loop construct can iterate over any container. In these cases, the alternative @@ -475,7 +487,7 @@ julia> i = 1; julia> while true println(i) - if i >= 5 + if i >= 3 break end global i += 1 @@ -483,20 +495,16 @@ julia> while true 1 2 3 -4 -5 julia> for j = 1:1000 println(j) - if j >= 5 + if j >= 3 break end end 1 2 3 -4 -5 ``` Without the `break` keyword, the above `while` loop would never terminate on its own, and the `for` loop would iterate up to 1000. These loops are both exited early by using `break`. From 9a0e79723159e168099097cbe435826b4abe0597 Mon Sep 17 00:00:00 2001 From: Tomas Fiers Date: Wed, 11 May 2022 19:03:04 +0100 Subject: [PATCH 24/38] docs: Improve discoverability of `atreplinit` (mention it in Manual>"Command-line Interface">"Startup file") (#44103) --- doc/make.jl | 2 +- ...e-options.md => command-line-interface.md} | 20 +++++++++++++++++-- doc/src/manual/getting-started.md | 3 +-- 3 files changed, 20 insertions(+), 5 deletions(-) rename doc/src/manual/{command-line-options.md => command-line-interface.md} (96%) diff --git a/doc/make.jl b/doc/make.jl index 972da3d7e3891..4c1ccaa3a6e73 100644 --- a/doc/make.jl +++ b/doc/make.jl @@ -100,7 +100,7 @@ Manual = [ "manual/faq.md", "manual/noteworthy-differences.md", "manual/unicode-input.md", - "manual/command-line-options.md", + "manual/command-line-interface.md", ] BaseDocs = [ diff --git a/doc/src/manual/command-line-options.md b/doc/src/manual/command-line-interface.md similarity index 96% rename from doc/src/manual/command-line-options.md rename to doc/src/manual/command-line-interface.md index 3839e503ab4bb..1c281a67e55e9 100644 --- a/doc/src/manual/command-line-options.md +++ b/doc/src/manual/command-line-interface.md @@ -1,4 +1,4 @@ -# [Command-line Options](@id command-line-options) +# Command-line Interface ## Using arguments inside scripts @@ -39,6 +39,9 @@ $ julia --color=yes -O -- script.jl arg1 arg2.. See also [Scripting](@ref man-scripting) for more information on writing Julia scripts. + +## Parallel mode + Julia can be started in parallel mode with either the `-p` or the `--machine-file` options. `-p n` will launch an additional `n` worker processes, while `--machine-file file` will launch a worker for each line in file `file`. The machines defined in `file` must be accessible via a password-less @@ -48,6 +51,9 @@ takes the form `[count*][user@]host[:port] [bind_addr[:port]]`. `user` defaults to 1. The optional `bind-to bind_addr[:port]` specifies the IP address and port that other workers should use to connect to this worker. + +## Startup file + If you have code that you want executed whenever Julia is run, you can put it in `~/.julia/config/startup.jl`: @@ -63,7 +69,17 @@ Note that although you should have a `~/.julia` directory once you've run Julia first time, you may need to create the `~/.julia/config` folder and the `~/.julia/config/startup.jl` file if you use it. -## Command-line switches for Julia +To have startup code run only in [The Julia REPL] (and not when `julia` is *e.g.* run +on a script), use [`atreplinit`](@ref) in `startup.jl`: + +```julia +atreplinit() do repl + # ... +end +``` + + +## [Command-line switches for Julia](@id command-line-options) There are various ways to run Julia code and provide options, similar to those available for the `perl` and `ruby` programs: diff --git a/doc/src/manual/getting-started.md b/doc/src/manual/getting-started.md index a3a92c6d7c93c..16dab24afecf9 100644 --- a/doc/src/manual/getting-started.md +++ b/doc/src/manual/getting-started.md @@ -34,8 +34,7 @@ command: $ julia script.jl ``` -You can pass additional arguments to Julia, and to your program `script.jl`. A detailed list of all the available switches can be found at [Command-line Options](@ref -command-line-options). +You can pass additional arguments to Julia, and to your program `script.jl`. A detailed list of all the available options can be found under [Command-line Interface](@ref). ## Resources From bf2c2e8b211d648d0b477596cab75ba6cb978319 Mon Sep 17 00:00:00 2001 From: pchintalapudi <34727397+pchintalapudi@users.noreply.github.com> Date: Wed, 11 May 2022 15:32:01 -0400 Subject: [PATCH 25/38] Make dump_llvm_ir C-api compatible (#45252) --- src/aotcompile.cpp | 8 +++--- src/codegen.cpp | 2 +- src/disasm.cpp | 68 ++++++++++++++++++++++++++-------------------- src/jitlayers.cpp | 4 +-- src/jitlayers.h | 7 +++-- 5 files changed, 51 insertions(+), 38 deletions(-) diff --git a/src/aotcompile.cpp b/src/aotcompile.cpp index 0df8b9047e14e..b9d4b2b3e896d 100644 --- a/src/aotcompile.cpp +++ b/src/aotcompile.cpp @@ -117,7 +117,7 @@ LLVMOrcThreadSafeModuleRef jl_get_llvm_module_impl(void *native_code) { jl_native_code_desc_t *data = (jl_native_code_desc_t*)native_code; if (data) - return reinterpret_cast(&data->M); + return wrap(&data->M); else return NULL; } @@ -264,7 +264,7 @@ void *jl_create_native_impl(jl_array_t *methods, LLVMOrcThreadSafeModuleRef llvm ctx = jl_ExecutionEngine->acquireContext(); backing = jl_create_llvm_module("text", ctx, imaging); } - orc::ThreadSafeModule &clone = llvmmod ? *reinterpret_cast(llvmmod) : backing; + orc::ThreadSafeModule &clone = llvmmod ? *unwrap(llvmmod) : backing; auto ctxt = clone.getContext(); jl_codegen_params_t params(ctxt); params.params = cgparams; @@ -291,7 +291,7 @@ void *jl_create_native_impl(jl_array_t *methods, LLVMOrcThreadSafeModuleRef llvm jl_value_t *item = jl_array_ptr_ref(methods, i); if (jl_is_simplevector(item)) { if (worlds == 1) - jl_compile_extern_c(reinterpret_cast(&clone), ¶ms, NULL, jl_svecref(item, 0), jl_svecref(item, 1)); + jl_compile_extern_c(wrap(&clone), ¶ms, NULL, jl_svecref(item, 0), jl_svecref(item, 1)); continue; } mi = (jl_method_instance_t*)item; @@ -1067,7 +1067,7 @@ void *jl_get_llvmf_defn_impl(jl_method_instance_t *mi, size_t world, char getwra jl_atomic_fetch_add_relaxed(&jl_cumulative_compile_time, (jl_hrtime() - compiler_start_time)); JL_UNLOCK(&jl_codegen_lock); // Might GC if (F) - return new jl_llvmf_dump_t{std::move(m), F}; + return new jl_llvmf_dump_t{wrap(new orc::ThreadSafeModule(std::move(m))), wrap(F)}; } const char *mname = name_from_method_instance(mi); diff --git a/src/codegen.cpp b/src/codegen.cpp index a95631962e93d..648b68305daec 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -6193,7 +6193,7 @@ const char *jl_generate_ccallable(LLVMOrcThreadSafeModuleRef llvmmod, void *sysi else { jl_method_instance_t *lam = jl_get_specialization1((jl_tupletype_t*)sigt, world, &min_valid, &max_valid, 0); //Safe b/c params holds context lock - gen_cfun_wrapper(reinterpret_cast(llvmmod)->getModuleUnlocked(), params, sig, ff, name, declrt, lam, NULL, NULL, NULL); + gen_cfun_wrapper(unwrap(llvmmod)->getModuleUnlocked(), params, sig, ff, name, declrt, lam, NULL, NULL, NULL); } JL_GC_POP(); return name; diff --git a/src/disasm.cpp b/src/disasm.cpp index 4d21f49e4af2a..ab1741abf38b4 100644 --- a/src/disasm.cpp +++ b/src/disasm.cpp @@ -496,34 +496,43 @@ jl_value_t *jl_dump_function_ir_impl(void *f, char strip_ir_metadata, char dump_ raw_string_ostream stream(code); { + //RAII will release the struct itself std::unique_ptr dump(static_cast(f)); - dump->TSM.withModuleDo([&](Module &m) { - Function *llvmf = dump->F; - if (!llvmf || (!llvmf->isDeclaration() && !llvmf->getParent())) - jl_error("jl_dump_function_ir: Expected Function* in a temporary Module"); - - LineNumberAnnotatedWriter AAW{"; ", false, debuginfo}; - if (!llvmf->getParent()) { - // print the function declaration as-is - llvmf->print(stream, &AAW); - delete llvmf; + //RAII will release the module + auto TSM = std::unique_ptr(unwrap(dump->TSM)); + //If TSM is not passed in, then the context MUST be locked externally. + //RAII will release the lock + Optional lock; + if (TSM) { + lock.emplace(TSM->getContext().getLock()); + } + Function *llvmf = cast(unwrap(dump->F)); + if (!llvmf || (!llvmf->isDeclaration() && !llvmf->getParent())) + jl_error("jl_dump_function_ir: Expected Function* in a temporary Module"); + + LineNumberAnnotatedWriter AAW{"; ", false, debuginfo}; + if (!llvmf->getParent()) { + // print the function declaration as-is + llvmf->print(stream, &AAW); + delete llvmf; + } + else { + assert(TSM && TSM->getModuleUnlocked() == llvmf->getParent() && "Passed module was not the same as function parent!"); + auto m = TSM->getModuleUnlocked(); + if (strip_ir_metadata) { + std::string llvmfn(llvmf->getName()); + jl_strip_llvm_addrspaces(m); + jl_strip_llvm_debug(m, true, &AAW); + // rewriting the function type creates a new function, so look it up again + llvmf = m->getFunction(llvmfn); + } + if (dump_module) { + m->print(stream, &AAW); } else { - if (strip_ir_metadata) { - std::string llvmfn(llvmf->getName()); - jl_strip_llvm_addrspaces(&m); - jl_strip_llvm_debug(&m, true, &AAW); - // rewriting the function type creates a new function, so look it up again - llvmf = m.getFunction(llvmfn); - } - if (dump_module) { - m.print(stream, &AAW); - } - else { - llvmf->print(stream, &AAW); - } + llvmf->print(stream, &AAW); } - }); + } } return jl_pchar_to_string(stream.str().data(), stream.str().size()); @@ -1200,10 +1209,11 @@ jl_value_t *jl_dump_function_asm_impl(void *F, char raw_mc, const char* asm_vari SmallVector ObjBufferSV; { // scope block std::unique_ptr dump(static_cast(F)); - Function *f = dump->F; + auto TSM = std::unique_ptr(unwrap(dump->TSM)); llvm::raw_svector_ostream asmfile(ObjBufferSV); - assert(!f->isDeclaration()); - dump->TSM.withModuleDo([&](Module &m) { + TSM->withModuleDo([&](Module &m) { + Function *f = cast(unwrap(dump->F)); + assert(!f->isDeclaration()); for (auto &f2 : m.functions()) { if (f != &f2 && !f->isDeclaration()) f2.deleteBody(); @@ -1217,7 +1227,7 @@ jl_value_t *jl_dump_function_asm_impl(void *F, char raw_mc, const char* asm_vari raw_svector_ostream obj_OS(ObjBufferSV); if (TM->addPassesToEmitFile(PM, obj_OS, nullptr, CGFT_ObjectFile, false, nullptr)) return jl_an_empty_string; - dump->TSM.withModuleDo([&](Module &m) { PM.run(m); }); + TSM->withModuleDo([&](Module &m) { PM.run(m); }); } else { MCContext *Context = addPassesToGenerateCode(TM, PM); @@ -1261,7 +1271,7 @@ jl_value_t *jl_dump_function_asm_impl(void *F, char raw_mc, const char* asm_vari return jl_an_empty_string; PM.add(Printer.release()); PM.add(createFreeMachineFunctionPass()); - dump->TSM.withModuleDo([&](Module &m){ PM.run(m); }); + TSM->withModuleDo([&](Module &m){ PM.run(m); }); } } return jl_pchar_to_string(ObjBufferSV.data(), ObjBufferSV.size()); diff --git a/src/jitlayers.cpp b/src/jitlayers.cpp index 1781b0a6a6fe2..7592a15c927de 100644 --- a/src/jitlayers.cpp +++ b/src/jitlayers.cpp @@ -226,7 +226,7 @@ int jl_compile_extern_c_impl(LLVMOrcThreadSafeModuleRef llvmmod, void *p, void * if (measure_compile_time_enabled) compiler_start_time = jl_hrtime(); orc::ThreadSafeContext ctx; - auto into = reinterpret_cast(llvmmod); + auto into = unwrap(llvmmod); jl_codegen_params_t *pparams = (jl_codegen_params_t*)p; orc::ThreadSafeModule backing; if (into == NULL) { @@ -240,7 +240,7 @@ int jl_compile_extern_c_impl(LLVMOrcThreadSafeModuleRef llvmmod, void *p, void * if (pparams == NULL) pparams = ¶ms; assert(pparams->tsctx.getContext() == into->getContext().getContext()); - const char *name = jl_generate_ccallable(reinterpret_cast(into), sysimg, declrt, sigt, *pparams); + const char *name = jl_generate_ccallable(wrap(into), sysimg, declrt, sigt, *pparams); bool success = true; if (!sysimg) { if (jl_ExecutionEngine->getGlobalValueAddress(name)) { diff --git a/src/jitlayers.h b/src/jitlayers.h index 88129b65e8dd2..05f240a5848e0 100644 --- a/src/jitlayers.h +++ b/src/jitlayers.h @@ -54,6 +54,9 @@ using namespace llvm; extern "C" jl_cgparams_t jl_default_cgparams; +DEFINE_SIMPLE_CONVERSION_FUNCTIONS(orc::ThreadSafeContext, LLVMOrcThreadSafeContextRef) +DEFINE_SIMPLE_CONVERSION_FUNCTIONS(orc::ThreadSafeModule, LLVMOrcThreadSafeModuleRef) + void addTargetPasses(legacy::PassManagerBase *PM, const Triple &triple, TargetIRAnalysis analysis); void addOptimizationPasses(legacy::PassManagerBase *PM, int opt_level, bool lower_intrinsics=true, bool dump_native=false, bool external_use=false); void addMachinePasses(legacy::PassManagerBase *PM, int optlevel); @@ -115,8 +118,8 @@ struct jl_returninfo_t { }; struct jl_llvmf_dump_t { - orc::ThreadSafeModule TSM; - Function *F; + LLVMOrcThreadSafeModuleRef TSM; + LLVMValueRef F; }; typedef std::tuple jl_codegen_call_target_t; From 93ac4a310b4c771c8e7920647486650b78d2c7fa Mon Sep 17 00:00:00 2001 From: Gabriel Baraldi Date: Wed, 11 May 2022 21:15:36 -0300 Subject: [PATCH 26/38] Hoist object allocation before inner field initialization (#45153) * Hoist object allocation before inner field initialization Consider the following pattern for building up nested objects ``` %obj = Expr(:new, obj, ...) %obj_wrapper = Expr(:new, obj_wrapper, ..., %obj) %obj_wrapper2 = Expr(:new, obj_wrapper2, ..., %obj_wrapper) %outer = Expr(:new, outer, %obj_wrapper2) ``` Asssuming everything except `struct outer` is struct-inlineable, the LLVM IR we emit looks something like the following: ``` %obj = alloca %obj_wrapper = alloca %obj_wrapper_wrapper = alloca %obj = alloca init(%obj, ) init(%obj_wrapper, ); memcpy(%obj_wrapper, %obj) init(%obj_wrapper2, ); memcpy(%obj_wrapper2, %obj_wrapper) init(%outer, ); memcpy(%outer, %obj_wrapper2) %outer_boxed = julia.gc_alloc memcpy(%outer_boxed, %outer) ``` While LLVM is capable of removing all the allocas and memcpys, it's taking an unreasonable amount of time to do so. This PR introduces a small optimization into the frontend lowering for `:new`: If all the :new calls are in the same LLVM basic block, then we delete the alloca, and hoist the allocation of the object to the earliest point before the initialization of the fields. This gives essentially the same result as LLVM would have given us post-optimization, but is much cheaper to do because we don't have to perform any analysis to tell us that it is a legal optimization. In the above example, we would end up with something like: ``` %outer_boxed = julia.gc_alloc init(%outer_boxed, ) init(%outer_boxed, ); init(%outer_boxed, ); init(%outer_boxed, ); ``` Of course this does extend the liftime of the outer object, but I don't think that's a particular problem as long as we're careful not to hoist any boxings out of error paths. In the current implementation, I only allow this optimization to occur in the same LLVM basic block, but I think it should be fine to extend it to allow the same julia basic block or more generally, any allocation that post-dominates the relevant promotion points. * fix codegen bug Co-authored-by: Keno Fischer --- src/cgutils.cpp | 104 +++++++++++++++++++++++++++++++--- src/codegen.cpp | 144 +++++++++++++++++++++++++++++++++--------------- 2 files changed, 197 insertions(+), 51 deletions(-) diff --git a/src/cgutils.cpp b/src/cgutils.cpp index 655fe33eff294..57b57a5074cc2 100644 --- a/src/cgutils.cpp +++ b/src/cgutils.cpp @@ -3163,6 +3163,33 @@ static Value *box_union(jl_codectx_t &ctx, const jl_cgval_t &vinfo, const SmallB return box_merge; } +static void recursively_adjust_ptr_type(llvm::Value *Val, unsigned FromAS, unsigned ToAS) +{ + for (auto *User : Val->users()) { + if (isa(User)) { + GetElementPtrInst *Inst = cast(User); + Inst->mutateType(PointerType::getWithSamePointeeType(cast(Inst->getType()), ToAS)); + recursively_adjust_ptr_type(Inst, FromAS, ToAS); + } + else if (isa(User)) { + IntrinsicInst *II = cast(User); + SmallVector ArgTys; + Intrinsic::getIntrinsicSignature(II->getCalledFunction(), ArgTys); + assert(ArgTys.size() <= II->arg_size()); + for (size_t i = 0; i < ArgTys.size(); ++i) + ArgTys[i] = II->getArgOperand(i)->getType(); + II->setCalledFunction(Intrinsic::getDeclaration(II->getModule(), II->getIntrinsicID(), ArgTys)); + } +#ifndef JL_LLVM_OPAQUE_POINTERS + else if (isa(User)) { + BitCastInst *Inst = cast(User); + Inst->mutateType(PointerType::getWithSamePointeeType(cast(Inst->getType()), ToAS)); + recursively_adjust_ptr_type(Inst, FromAS, ToAS); + } +#endif + } +} + // this is used to wrap values for generic contexts, where a // dynamically-typed value is required (e.g. argument to unknown function). // if it's already a pointer it's left alone. @@ -3196,8 +3223,27 @@ static Value *boxed(jl_codectx_t &ctx, const jl_cgval_t &vinfo) assert(!type_is_ghost(t)); // ghost values should have been handled by vinfo.constant above! box = _boxed_special(ctx, vinfo, t); if (!box) { - box = emit_allocobj(ctx, jl_datatype_size(jt), literal_pointer_val(ctx, (jl_value_t*)jt)); - init_bits_cgval(ctx, box, vinfo, jl_is_mutable(jt) ? ctx.tbaa().tbaa_mutab : ctx.tbaa().tbaa_immut); + bool do_promote = vinfo.promotion_point; + if (do_promote) { + auto IP = ctx.builder.saveIP(); + ctx.builder.SetInsertPoint(vinfo.promotion_point); + box = emit_allocobj(ctx, jl_datatype_size(jt), literal_pointer_val(ctx, (jl_value_t*)jt)); + Value *decayed = decay_derived(ctx, box); + AllocaInst *originalAlloca = cast(vinfo.V); +#ifndef JL_LLVM_OPAQUE_POINTERS + decayed = maybe_bitcast(ctx, decayed, PointerType::get(originalAlloca->getType()->getPointerElementType(), AddressSpace::Derived)); +#endif + // Warning: Very illegal IR here temporarily + originalAlloca->mutateType(decayed->getType()); + recursively_adjust_ptr_type(originalAlloca, 0, AddressSpace::Derived); + originalAlloca->replaceAllUsesWith(decayed); + // end illegal IR + cast(vinfo.V)->eraseFromParent(); + ctx.builder.restoreIP(IP); + } else { + box = emit_allocobj(ctx, jl_datatype_size(jt), literal_pointer_val(ctx, (jl_value_t*)jt)); + init_bits_cgval(ctx, box, vinfo, jl_is_mutable(jt) ? ctx.tbaa().tbaa_mutab : ctx.tbaa().tbaa_immut); + } } } return box; @@ -3511,7 +3557,7 @@ static jl_cgval_t emit_setfield(jl_codectx_t &ctx, } } -static jl_cgval_t emit_new_struct(jl_codectx_t &ctx, jl_value_t *ty, size_t nargs, const jl_cgval_t *argv) +static jl_cgval_t emit_new_struct(jl_codectx_t &ctx, jl_value_t *ty, size_t nargs, const jl_cgval_t *argv, bool is_promotable) { ++EmittedNewStructs; assert(jl_is_datatype(ty)); @@ -3534,6 +3580,8 @@ static jl_cgval_t emit_new_struct(jl_codectx_t &ctx, jl_value_t *ty, size_t narg init_as_value = true; } + Instruction *promotion_point = nullptr; + ssize_t promotion_ssa = -1; Value *strct; if (type_is_ghost(lt)) { strct = NULL; @@ -3553,8 +3601,17 @@ static jl_cgval_t emit_new_struct(jl_codectx_t &ctx, jl_value_t *ty, size_t narg for (unsigned i = 0; i < na; i++) { jl_value_t *jtype = jl_svecref(sty->types, i); // n.b. ty argument must be concrete jl_cgval_t fval_info = argv[i]; + + IRBuilderBase::InsertPoint savedIP; emit_typecheck(ctx, fval_info, jtype, "new"); fval_info = update_julia_type(ctx, fval_info, jtype); + // TODO: Use (post-)domination instead. + bool field_promotable = !init_as_value && fval_info.promotion_ssa != -1 && + fval_info.promotion_point && fval_info.promotion_point->getParent() == ctx.builder.GetInsertBlock(); + if (field_promotable) { + savedIP = ctx.builder.saveIP(); + ctx.builder.SetInsertPoint(fval_info.promotion_point); + } if (type_is_ghost(lt)) continue; Type *fty = julia_type_to_llvm(ctx, jtype); @@ -3566,7 +3623,25 @@ static jl_cgval_t emit_new_struct(jl_codectx_t &ctx, jl_value_t *ty, size_t narg if (!init_as_value) { // avoid unboxing the argument explicitly // and use memcpy instead - dest = ctx.builder.CreateConstInBoundsGEP2_32(lt, strct, 0, llvm_idx); + Instruction *inst; +#ifndef JL_LLVM_OPAQUE_POINTERS + dest = inst = cast(ctx.builder.CreateConstInBoundsGEP2_32(lt, strct, 0, llvm_idx)); +#else + dest = inst = cast(ctx.builder.CreateConstInBoundsGEP1_32(getInt8Ty(ctx.builder.getContext()), strct, offs)); +#endif + // Our promotion point needs to come before + // A) All of our arguments' promotion points + // B) Any instructions we insert at any of our arguments' promotion points + // N.B.: Do not use Instruction::comesBefore here. LLVM invalidates its instruction numbering after + // every insert, so querying it here makes code generation accidentally quadartic. + if (field_promotable) { + if (promotion_ssa == -1 || fval_info.promotion_ssa < promotion_ssa) { + promotion_point = inst; + promotion_ssa = fval_info.promotion_ssa; + } + } else if (!promotion_point) { + promotion_point = inst; + } } Value *fval = NULL; if (jl_field_isptr(sty, i)) { @@ -3577,6 +3652,7 @@ static jl_cgval_t emit_new_struct(jl_codectx_t &ctx, jl_value_t *ty, size_t narg ->setOrdering(AtomicOrdering::Unordered); } else if (jl_is_uniontype(jtype)) { + assert(!field_promotable); // compute tindex from rhs jl_cgval_t rhs_union = convert_julia_type(ctx, fval_info, jtype); if (rhs_union.typ == jl_bottom_type) @@ -3629,7 +3705,12 @@ static jl_cgval_t emit_new_struct(jl_codectx_t &ctx, jl_value_t *ty, size_t narg } } else { - fval = emit_unbox(ctx, fty, fval_info, jtype, dest, ctx.tbaa().tbaa_stack); + if (field_promotable) { + fval_info.V->replaceAllUsesWith(dest); + cast(fval_info.V)->eraseFromParent(); + } else { + fval = emit_unbox(ctx, fty, fval_info, jtype, dest, ctx.tbaa().tbaa_stack); + } } if (init_as_value) { assert(fval); @@ -3642,6 +3723,9 @@ static jl_cgval_t emit_new_struct(jl_codectx_t &ctx, jl_value_t *ty, size_t narg else assert(false); } + if (field_promotable) { + ctx.builder.restoreIP(savedIP); + } } for (size_t i = nargs; i < nf; i++) { if (!jl_field_isptr(sty, i) && jl_is_uniontype(jl_field_type(sty, i))) { @@ -3661,8 +3745,14 @@ static jl_cgval_t emit_new_struct(jl_codectx_t &ctx, jl_value_t *ty, size_t narg return mark_julia_const(ctx, sty->instance); else if (init_as_value) return mark_julia_type(ctx, strct, false, ty); - else - return mark_julia_slot(strct, ty, NULL, ctx.tbaa(), ctx.tbaa().tbaa_stack); + else { + jl_cgval_t ret = mark_julia_slot(strct, ty, NULL, ctx.tbaa(), ctx.tbaa().tbaa_stack); + if (is_promotable && promotion_point) { + ret.promotion_point = promotion_point; + ret.promotion_ssa = promotion_ssa; + } + return ret; + } } Value *strct = emit_allocobj(ctx, jl_datatype_size(sty), literal_pointer_val(ctx, (jl_value_t*)ty)); diff --git a/src/codegen.cpp b/src/codegen.cpp index 648b68305daec..fdf422bb07a7d 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -1217,6 +1217,13 @@ struct jl_cgval_t { bool isboxed; // whether this value is a jl_value_t* allocated on the heap with the right type tag bool isghost; // whether this value is "ghost" MDNode *tbaa; // The related tbaa node. Non-NULL iff this holds an address. + // If non-null, this memory location may be promoted on use, by hoisting the + // destination memory above the promotion point. + Instruction *promotion_point; + // If promotion_ssa is non-null, the julia src ssa value that corresponds + // to the promotion point. This is used for dominator analysis, since LLVM's + // dominator analysis has algorithmic problems for large basic blocks. + ssize_t promotion_ssa; bool ispointer() const { // whether this value is compatible with `data_pointer` @@ -1230,7 +1237,9 @@ struct jl_cgval_t { typ(typ), isboxed(isboxed), isghost(false), - tbaa(isboxed ? best_tbaa(tbaa_cache, typ) : nullptr) + tbaa(isboxed ? best_tbaa(tbaa_cache, typ) : nullptr), + promotion_point(nullptr), + promotion_ssa(-1) { if (Vboxed) assert(Vboxed->getType() == JuliaType::get_prjlvalue_ty(Vboxed->getContext())); @@ -1247,7 +1256,9 @@ struct jl_cgval_t { typ(typ), isboxed(false), isghost(true), - tbaa(nullptr) + tbaa(nullptr), + promotion_point(nullptr), + promotion_ssa(-1) { assert(jl_is_datatype(typ)); assert(constant); @@ -1260,7 +1271,9 @@ struct jl_cgval_t { typ(typ), isboxed(v.isboxed), isghost(v.isghost), - tbaa(v.tbaa) + tbaa(v.tbaa), + promotion_point(v.promotion_point), + promotion_ssa(v.promotion_ssa) { if (Vboxed) assert(Vboxed->getType() == JuliaType::get_prjlvalue_ty(Vboxed->getContext())); @@ -1281,7 +1294,9 @@ struct jl_cgval_t { typ(jl_bottom_type), isboxed(false), isghost(true), - tbaa(nullptr) + tbaa(nullptr), + promotion_point(nullptr), + promotion_ssa(-1) { } }; @@ -1330,6 +1345,7 @@ class jl_codectx_t { std::vector SAvalues; std::vector> PhiNodes; std::vector ssavalue_assigned; + std::vector ssavalue_usecount; std::vector oc_modules; jl_module_t *module = NULL; jl_typecache_t type_cache; @@ -1413,7 +1429,7 @@ static CallInst *emit_jlcall(jl_codectx_t &ctx, JuliaFunction *theFptr, Value *t const jl_cgval_t *args, size_t nargs, CallingConv::ID cc); static Value *emit_f_is(jl_codectx_t &ctx, const jl_cgval_t &arg1, const jl_cgval_t &arg2, Value *nullcheck1 = nullptr, Value *nullcheck2 = nullptr); -static jl_cgval_t emit_new_struct(jl_codectx_t &ctx, jl_value_t *ty, size_t nargs, const jl_cgval_t *argv); +static jl_cgval_t emit_new_struct(jl_codectx_t &ctx, jl_value_t *ty, size_t nargs, const jl_cgval_t *argv, bool is_promotable=false); static jl_cgval_t emit_invoke(jl_codectx_t &ctx, const jl_cgval_t &lival, const jl_cgval_t *argv, size_t nargs, jl_value_t *rt); static Value *literal_pointer_val(jl_codectx_t &ctx, jl_value_t *p); @@ -2376,54 +2392,55 @@ static void mark_volatile_vars(jl_array_t *stmts, std::vector &slo // a very simple, conservative use analysis // to eagerly remove slot assignments that are never read from -static void simple_use_analysis(jl_codectx_t &ctx, jl_value_t *expr) + +template +static void general_use_analysis(jl_codectx_t &ctx, jl_value_t *expr, callback &f) { - if (jl_is_slot(expr) || jl_is_argument(expr)) { - int i = jl_slot_number(expr) - 1; - ctx.slots[i].used = true; + if (f(expr)) { + return; } else if (jl_is_expr(expr)) { jl_expr_t *e = (jl_expr_t*)expr; if (e->head == jl_method_sym) { - simple_use_analysis(ctx, jl_exprarg(e, 0)); + general_use_analysis(ctx, jl_exprarg(e, 0), f); if (jl_expr_nargs(e) > 1) { - simple_use_analysis(ctx, jl_exprarg(e, 1)); - simple_use_analysis(ctx, jl_exprarg(e, 2)); + general_use_analysis(ctx, jl_exprarg(e, 1), f); + general_use_analysis(ctx, jl_exprarg(e, 2), f); } } else if (e->head == jl_assign_sym) { // don't consider assignment LHS as a variable "use" - simple_use_analysis(ctx, jl_exprarg(e, 1)); + general_use_analysis(ctx, jl_exprarg(e, 1), f); } else { size_t i, elen = jl_array_dim0(e->args); for (i = 0; i < elen; i++) { - simple_use_analysis(ctx, jl_exprarg(e, i)); + general_use_analysis(ctx, jl_exprarg(e, i), f); } } } else if (jl_is_returnnode(expr)) { jl_value_t *retexpr = jl_returnnode_value(expr); if (retexpr != NULL) - simple_use_analysis(ctx, retexpr); + general_use_analysis(ctx, retexpr, f); } else if (jl_is_gotoifnot(expr)) { - simple_use_analysis(ctx, jl_gotoifnot_cond(expr)); + general_use_analysis(ctx, jl_gotoifnot_cond(expr), f); } else if (jl_is_pinode(expr)) { - simple_use_analysis(ctx, jl_fieldref_noalloc(expr, 0)); + general_use_analysis(ctx, jl_fieldref_noalloc(expr, 0), f); } else if (jl_is_upsilonnode(expr)) { jl_value_t *val = jl_fieldref_noalloc(expr, 0); if (val) - simple_use_analysis(ctx, val); + general_use_analysis(ctx, val, f); } else if (jl_is_phicnode(expr)) { jl_array_t *values = (jl_array_t*)jl_fieldref_noalloc(expr, 0); size_t i, elen = jl_array_len(values); for (i = 0; i < elen; i++) { jl_value_t *v = jl_array_ptr_ref(values, i); - simple_use_analysis(ctx, v); + general_use_analysis(ctx, v, f); } } else if (jl_is_phinode(expr)) { @@ -2432,11 +2449,24 @@ static void simple_use_analysis(jl_codectx_t &ctx, jl_value_t *expr) for (i = 0; i < elen; i++) { jl_value_t *v = jl_array_ptr_ref(values, i); if (v) - simple_use_analysis(ctx, v); + general_use_analysis(ctx, v, f); } } } +static void simple_use_analysis(jl_codectx_t &ctx, jl_value_t *expr) +{ + auto scan_slot_arg = [&](jl_value_t *expr) { + if (jl_is_slot(expr) || jl_is_argument(expr)) { + int i = jl_slot_number(expr) - 1; + ctx.slots[i].used = true; + return true; + } + return false; + }; + return general_use_analysis(ctx, expr, scan_slot_arg); +} + // --- gc root utils --- // ---- Get Element Pointer (GEP) instructions within the GC frame ---- @@ -2931,7 +2961,7 @@ static jl_llvm_functions_t static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, const jl_cgval_t *argv, size_t nargs, jl_value_t *rt, - jl_expr_t *ex) + jl_expr_t *ex, bool is_promotable) // returns true if the call has been handled { ++EmittedBuiltinCalls; @@ -3012,7 +3042,7 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, return true; } if (jl_is_tuple_type(rt) && jl_is_concrete_type(rt) && nargs == jl_datatype_nfields(rt)) { - *ret = emit_new_struct(ctx, rt, nargs, &argv[1]); + *ret = emit_new_struct(ctx, rt, nargs, &argv[1], is_promotable); return true; } } @@ -3987,7 +4017,7 @@ static jl_cgval_t emit_invoke_modify(jl_codectx_t &ctx, jl_expr_t *ex, jl_value_ return mark_julia_type(ctx, callval, true, rt); } -static jl_cgval_t emit_call(jl_codectx_t &ctx, jl_expr_t *ex, jl_value_t *rt) +static jl_cgval_t emit_call(jl_codectx_t &ctx, jl_expr_t *ex, jl_value_t *rt, bool is_promotable) { ++EmittedCalls; jl_value_t **args = (jl_value_t**)jl_array_data(ex->args); @@ -4020,7 +4050,7 @@ static jl_cgval_t emit_call(jl_codectx_t &ctx, jl_expr_t *ex, jl_value_t *rt) if (f.constant == jl_builtin_ifelse && nargs == 4) return emit_ifelse(ctx, argv[1], argv[2], argv[3], rt); jl_cgval_t result(ctx.builder.getContext()); - bool handled = emit_builtin_call(ctx, &result, f.constant, argv, nargs - 1, rt, ex); + bool handled = emit_builtin_call(ctx, &result, f.constant, argv, nargs - 1, rt, ex, is_promotable); if (handled) { return result; } @@ -4484,36 +4514,36 @@ static void emit_phinode_assign(jl_codectx_t &ctx, ssize_t idx, jl_value_t *r) return; } -static void emit_ssaval_assign(jl_codectx_t &ctx, ssize_t idx, jl_value_t *r) +static void emit_ssaval_assign(jl_codectx_t &ctx, ssize_t ssaidx_0based, jl_value_t *r) { - assert(!ctx.ssavalue_assigned.at(idx)); + assert(!ctx.ssavalue_assigned.at(ssaidx_0based)); if (jl_is_phinode(r)) { - return emit_phinode_assign(ctx, idx, r); + return emit_phinode_assign(ctx, ssaidx_0based, r); } jl_cgval_t slot(ctx.builder.getContext()); if (jl_is_phicnode(r)) { - auto it = ctx.phic_slots.find(idx); + auto it = ctx.phic_slots.find(ssaidx_0based); if (it == ctx.phic_slots.end()) { - it = ctx.phic_slots.emplace(idx, jl_varinfo_t(ctx.builder.getContext())).first; + it = ctx.phic_slots.emplace(ssaidx_0based, jl_varinfo_t(ctx.builder.getContext())).first; } slot = emit_varinfo(ctx, it->second, jl_symbol("phic")); } else { - slot = emit_expr(ctx, r, idx); // slot could be a jl_value_t (unboxed) or jl_value_t* (ispointer) + slot = emit_expr(ctx, r, ssaidx_0based); // slot could be a jl_value_t (unboxed) or jl_value_t* (ispointer) } if (slot.isboxed || slot.TIndex) { // see if inference suggested a different type for the ssavalue than the expression // e.g. sometimes the information is inconsistent after inlining getfield on a Tuple jl_value_t *ssavalue_types = (jl_value_t*)ctx.source->ssavaluetypes; if (jl_is_array(ssavalue_types)) { - jl_value_t *declType = jl_array_ptr_ref(ssavalue_types, idx); + jl_value_t *declType = jl_array_ptr_ref(ssavalue_types, ssaidx_0based); if (declType != slot.typ) { slot = update_julia_type(ctx, slot, declType); } } } - ctx.SAvalues.at(idx) = slot; // now SAvalues[idx] contains the SAvalue - ctx.ssavalue_assigned.at(idx) = true; + ctx.SAvalues.at(ssaidx_0based) = slot; // now SAvalues[ssaidx_0based] contains the SAvalue + ctx.ssavalue_assigned.at(ssaidx_0based) = true; } static void emit_varinfo_assign(jl_codectx_t &ctx, jl_varinfo_t &vi, jl_cgval_t rval_info, jl_value_t *l=NULL) @@ -4812,7 +4842,7 @@ static std::pair get_oc_function(jl_codectx_t &ctx, jl_met // `expr` is not clobbered in JL_TRY JL_GCC_IGNORE_START("-Wclobbered") -static jl_cgval_t emit_expr(jl_codectx_t &ctx, jl_value_t *expr, ssize_t ssaval) +static jl_cgval_t emit_expr(jl_codectx_t &ctx, jl_value_t *expr, ssize_t ssaidx_0based) { if (jl_is_symbol(expr)) { jl_sym_t *sym = (jl_sym_t*)expr; @@ -4905,27 +4935,33 @@ static jl_cgval_t emit_expr(jl_codectx_t &ctx, jl_value_t *expr, ssize_t ssaval) return ghostValue(ctx, jl_nothing_type); } else if (head == jl_invoke_sym) { - assert(ssaval >= 0); + assert(ssaidx_0based >= 0); jl_value_t *expr_t = jl_is_long(ctx.source->ssavaluetypes) ? (jl_value_t*)jl_any_type : - jl_array_ptr_ref(ctx.source->ssavaluetypes, ssaval); + jl_array_ptr_ref(ctx.source->ssavaluetypes, ssaidx_0based); return emit_invoke(ctx, ex, expr_t); } else if (head == jl_invoke_modify_sym) { - assert(ssaval >= 0); + assert(ssaidx_0based >= 0); jl_value_t *expr_t = jl_is_long(ctx.source->ssavaluetypes) ? (jl_value_t*)jl_any_type : - jl_array_ptr_ref(ctx.source->ssavaluetypes, ssaval); + jl_array_ptr_ref(ctx.source->ssavaluetypes, ssaidx_0based); return emit_invoke_modify(ctx, ex, expr_t); } else if (head == jl_call_sym) { jl_value_t *expr_t; - if (ssaval < 0) + bool is_promotable = false; + if (ssaidx_0based < 0) // TODO: this case is needed for the call to emit_expr in emit_llvmcall expr_t = (jl_value_t*)jl_any_type; - else - expr_t = jl_is_long(ctx.source->ssavaluetypes) ? (jl_value_t*)jl_any_type : jl_array_ptr_ref(ctx.source->ssavaluetypes, ssaval); - jl_cgval_t res = emit_call(ctx, ex, expr_t); + else { + expr_t = jl_is_long(ctx.source->ssavaluetypes) ? (jl_value_t*)jl_any_type : jl_array_ptr_ref(ctx.source->ssavaluetypes, ssaidx_0based); + is_promotable = ctx.ssavalue_usecount[ssaidx_0based] == 1; + } + jl_cgval_t res = emit_call(ctx, ex, expr_t, is_promotable); // some intrinsics (e.g. typeassert) can return a wider type // than what's actually possible + if (is_promotable && res.promotion_point) { + res.promotion_ssa = ssaidx_0based; + } res = update_julia_type(ctx, res, expr_t); if (res.typ == jl_bottom_type || expr_t == jl_bottom_type) { CreateTrap(ctx.builder); @@ -4942,7 +4978,7 @@ static jl_cgval_t emit_expr(jl_codectx_t &ctx, jl_value_t *expr, ssize_t ssaval) } else if (head == jl_assign_sym) { assert(nargs == 2); - emit_assignment(ctx, args[0], args[1], ssaval); + emit_assignment(ctx, args[0], args[1], ssaidx_0based); return ghostValue(ctx, jl_nothing_type); } else if (head == jl_static_parameter_sym) { @@ -5034,6 +5070,10 @@ static jl_cgval_t emit_expr(jl_codectx_t &ctx, jl_value_t *expr, ssize_t ssaval) } } else if (head == jl_new_sym) { + bool is_promotable = false; + if (ssaidx_0based >= 0) { + is_promotable = ctx.ssavalue_usecount[ssaidx_0based] == 1; + } assert(nargs > 0); jl_cgval_t *argv = (jl_cgval_t*)alloca(sizeof(jl_cgval_t) * nargs); for (size_t i = 0; i < nargs; ++i) { @@ -5044,7 +5084,10 @@ static jl_cgval_t emit_expr(jl_codectx_t &ctx, jl_value_t *expr, ssize_t ssaval) jl_is_datatype(jl_tparam0(ty)) && jl_is_concrete_type(jl_tparam0(ty))) { assert(nargs <= jl_datatype_nfields(jl_tparam0(ty)) + 1); - return emit_new_struct(ctx, jl_tparam0(ty), nargs - 1, &argv[1]); + jl_cgval_t res = emit_new_struct(ctx, jl_tparam0(ty), nargs - 1, &argv[1], is_promotable); + if (is_promotable && res.promotion_point && res.promotion_ssa==-1) + res.promotion_ssa = ssaidx_0based; + return res; } Value *val = emit_jlcall(ctx, jlnew_func, nullptr, argv, nargs, JLCALL_F_CC); // temporarily mark as `Any`, expecting `emit_ssaval_assign` to update @@ -6489,6 +6532,7 @@ static jl_datatype_t *compute_va_type(jl_method_instance_t *lam, size_t nreq) return typ; } + // Compile to LLVM IR, using a specialized signature if applicable. static jl_llvm_functions_t emit_function( @@ -6579,6 +6623,7 @@ static jl_llvm_functions_t // create SAvalue locations for SSAValue objects ctx.ssavalue_assigned.assign(n_ssavalues, false); ctx.SAvalues.assign(n_ssavalues, jl_cgval_t(ctx.builder.getContext())); + ctx.ssavalue_usecount.assign(n_ssavalues, 0); bool specsig, needsparams; std::tie(specsig, needsparams) = uses_specsig(lam, jlrettype, params.params->prefer_specsig); @@ -6980,9 +7025,20 @@ static jl_llvm_functions_t // Scan for PhiC nodes, emit their slots and record which upsilon nodes // yield to them. + // Also count ssavalue uses. { for (size_t i = 0; i < jl_array_len(stmts); ++i) { jl_value_t *stmt = jl_array_ptr_ref(stmts, i); + + auto scan_ssavalue = [&](jl_value_t *val) { + if (jl_is_ssavalue(val)) { + ctx.ssavalue_usecount[((jl_ssavalue_t*)val)->id-1] += 1; + return true; + } + return false; + }; + general_use_analysis(ctx, stmt, scan_ssavalue); + if (jl_is_phicnode(stmt)) { jl_array_t *values = (jl_array_t*)jl_fieldref_noalloc(stmt, 0); for (size_t j = 0; j < jl_array_len(values); ++j) { From 6611385c446b95ee6ee6e7205e86e6f9430092bb Mon Sep 17 00:00:00 2001 From: Mark Date: Thu, 12 May 2022 06:45:42 +0100 Subject: [PATCH 27/38] Minor English fixes/improvements (#29371) Co-authored-by: Kristoffer Carlsson --- doc/src/manual/embedding.md | 118 +++++++++++++++++++----------------- 1 file changed, 62 insertions(+), 56 deletions(-) diff --git a/doc/src/manual/embedding.md b/doc/src/manual/embedding.md index e4eac920865e5..26904d9ccffcd 100644 --- a/doc/src/manual/embedding.md +++ b/doc/src/manual/embedding.md @@ -1,16 +1,18 @@ # Embedding Julia -As we have seen in [Calling C and Fortran Code](@ref), Julia has a simple and efficient way to -call functions written in C. But there are situations where the opposite is needed: calling Julia -function from C code. This can be used to integrate Julia code into a larger C/C++ project, without -the need to rewrite everything in C/C++. Julia has a C API to make this possible. As almost all -programming languages have some way to call C functions, the Julia C API can also be used to build -further language bridges (e.g. calling Julia from Python or C#). +As we have seen in [Calling C and Fortran Code](@ref), Julia has a simple and efficient way +to call functions written in C. But there are situations where the opposite is needed: +calling Julia functions from C code. This can be used to integrate Julia code into a larger +C/C++ project, without the need to rewrite everything in C/C++. Julia has a C API to make +this possible. As almost all programming languages have some way to call C functions, the +Julia C API can also be used to build further language bridges (e.g. calling Julia from +Python or C#). ## High-Level Embedding -__Note__: This section covers embedding Julia code in C on Unix-like operating systems. For doing -this on Windows, please see the section following this. +__Note__: This section covers embedding Julia code in C on Unix-like operating systems. For +doing this on Windows, please see the section following this, +[High-Level Embedding on Windows with Visual Studio](@ref). We start with a simple C program that initializes Julia and calls some Julia code: @@ -36,9 +38,9 @@ int main(int argc, char *argv[]) } ``` -In order to build this program you have to put the path to the Julia header into the include path -and link against `libjulia`. For instance, when Julia is installed to `$JULIA_DIR`, one can compile -the above test program `test.c` with `gcc` using: +In order to build this program you must add the path to the Julia header to the include path +and link against `libjulia`. For instance, when Julia is installed to `$JULIA_DIR`, one can +compile the above test program `test.c` with `gcc` using: ``` gcc -o test -fPIC -I$JULIA_DIR/include/julia -L$JULIA_DIR/lib -Wl,-rpath,$JULIA_DIR/lib test.c -ljulia @@ -48,15 +50,15 @@ Alternatively, look at the `embedding.c` program in the Julia source tree in the The file `cli/loader_exe.c` program is another simple example of how to set `jl_options` options while linking against `libjulia`. -The first thing that has to be done before calling any other Julia C function is to initialize -Julia. This is done by calling `jl_init`, which tries to automatically determine Julia's install -location. If you need to specify a custom location, or specify which system image to load, -use `jl_init_with_image` instead. +The first thing that must be done before calling any other Julia C function is to +initialize Julia. This is done by calling `jl_init`, which tries to automatically determine +Julia's install location. If you need to specify a custom location, or specify which system +image to load, use `jl_init_with_image` instead. The second statement in the test program evaluates a Julia statement using a call to `jl_eval_string`. -Before the program terminates, it is strongly recommended to call `jl_atexit_hook`. The above -example program calls this before returning from `main`. +Before the program terminates, it is strongly recommended that `jl_atexit_hook` is called. +The above example program calls this just before returning from `main`. !!! note Currently, dynamically linking with the `libjulia` shared library requires passing the `RTLD_GLOBAL` @@ -70,17 +72,18 @@ example program calls this before returning from `main`. ``` !!! note - If the julia program needs to access symbols from the main executable, it may be necessary to - add `-Wl,--export-dynamic` linker flag at compile time on Linux in addition to the ones generated - by `julia-config.jl` described below. This is not necessary when compiling a shared library. + If the julia program needs to access symbols from the main executable, it may be + necessary to add the `-Wl,--export-dynamic` linker flag at compile time on Linux in + addition to the ones generated by `julia-config.jl` described below. This is not + necessary when compiling a shared library. ### Using julia-config to automatically determine build parameters -The script `julia-config.jl` was created to aid in determining what build parameters are required -by a program that uses embedded Julia. This script uses the build parameters and system configuration -of the particular Julia distribution it is invoked by to export the necessary compiler flags for -an embedding program to interact with that distribution. This script is located in the Julia -shared data directory. +The script `julia-config.jl` was created to aid in determining what build parameters are +required by a program that uses embedded Julia. This script uses the build parameters and +system configuration of the particular Julia distribution it is invoked by to export the +necessary compiler flags for an embedding program to interact with that distribution. This +script is located in the Julia shared data directory. #### Example @@ -98,18 +101,18 @@ int main(int argc, char *argv[]) #### On the command line -A simple use of this script is from the command line. Assuming that `julia-config.jl` is located -in `/usr/local/julia/share/julia`, it can be invoked on the command line directly and takes any -combination of 3 flags: +A simple use of this script is from the command line. Assuming that `julia-config.jl` is +located in `/usr/local/julia/share/julia`, it can be invoked on the command line directly +and takes any combination of three flags: ``` /usr/local/julia/share/julia/julia-config.jl Usage: julia-config [--cflags|--ldflags|--ldlibs] ``` -If the above example source is saved in the file `embed_example.c`, then the following command -will compile it into a running program on Linux and Windows (MSYS2 environment), or if on OS/X, -then substitute `clang` for `gcc`.: +If the above example source is saved in the file `embed_example.c`, then the following +command will compile it into an executable program on Linux and Windows (MSYS2 environment). +On macOS, substitute `clang` for `gcc`.: ``` /usr/local/julia/share/julia/julia-config.jl --cflags --ldflags --ldlibs | xargs gcc embed_example.c @@ -117,12 +120,12 @@ then substitute `clang` for `gcc`.: #### Use in Makefiles -But in general, embedding projects will be more complicated than the above, and so the following -allows general makefile support as well – assuming GNU make because of the use of the **shell** -macro expansions. Additionally, though many times `julia-config.jl` may be found in the directory -`/usr/local`, this is not necessarily the case, but Julia can be used to locate `julia-config.jl` -too, and the makefile can be used to take advantage of that. The above example is extended to -use a Makefile: +In general, embedding projects will be more complicated than the above example, and so the +following allows general makefile support as well – assuming GNU make because of the use of +the **shell** macro expansions. Furthermore, although `julia-config.jl` is usually in the +`/usr/local` directory, if it isn't, then Julia itself can be used to find +`julia-config.jl`, and the makefile can take advantage of this. The above example is +extended to use a makefile: ``` JL_SHARE = $(shell julia -e 'print(joinpath(Sys.BINDIR, Base.DATAROOTDIR, "julia"))') @@ -141,8 +144,8 @@ Now the build command is simply `make`. If the `JULIA_DIR` environment variable hasn't been setup, add it using the System panel before starting Visual Studio. The `bin` folder under JULIA_DIR should be on the system PATH. -We start by opening Visual Studio and creating a new Console Application project. To the 'stdafx.h' -header file, add the following lines at the end: +We start by opening Visual Studio and creating a new Console Application project. Open the +'stdafx.h' header file, and add the following lines at the end: ```c #include @@ -170,7 +173,7 @@ int main(int argc, char *argv[]) ``` The next step is to set up the project to find the Julia include files and the libraries. It's important to -know whether the Julia installation is 32- or 64-bits. Remove any platform configuration that doesn't correspond +know whether the Julia installation is 32- or 64-bit. Remove any platform configuration that doesn't correspond to the Julia installation before proceeding. Using the project Properties dialog, go to `C/C++` | `General` and add `$(JULIA_DIR)\include\julia\` to the @@ -182,11 +185,12 @@ At this point, the project should build and run. ## Converting Types -Real applications will not just need to execute expressions, but also return their values to the -host program. `jl_eval_string` returns a `jl_value_t*`, which is a pointer to a heap-allocated -Julia object. Storing simple data types like [`Float64`](@ref) in this way is called `boxing`, -and extracting the stored primitive data is called `unboxing`. Our improved sample program that -calculates the square root of 2 in Julia and reads back the result in C looks as follows: +Real applications will not only need to execute expressions, but also return their values to +the host program. `jl_eval_string` returns a `jl_value_t*`, which is a pointer to a +heap-allocated Julia object. Storing simple data types like [`Float64`](@ref) in this way is +called `boxing`, and extracting the stored primitive data is called `unboxing`. Our improved +sample program that calculates the square root of 2 in Julia and reads back the result in C +has a body that now contains this code: ```c jl_value_t *ret = jl_eval_string("sqrt(2.0)"); @@ -408,9 +412,10 @@ jl_checked_assignment(bp, val); ### Updating fields of GC-managed objects -The garbage collector operates under the assumption that it is aware of every old-generation -object pointing to a young-generation one. Any time a pointer is updated breaking that assumption, -it must be signaled to the collector with the `jl_gc_wb` (write barrier) function like so: +The garbage collector also operates under the assumption that it is aware of every +older-generation object pointing to a younger-generation one. Any time a pointer is updated +breaking that assumption, it must be signaled to the collector with the `jl_gc_wb` (write +barrier) function like so: ```c jl_value_t *parent = some_old_value, *child = some_young_value; @@ -418,10 +423,10 @@ jl_value_t *parent = some_old_value, *child = some_young_value; jl_gc_wb(parent, child); ``` -It is in general impossible to predict which values will be old at runtime, so the write barrier -must be inserted after all explicit stores. One notable exception is if the `parent` object was -just allocated and garbage collection was not run since then. Remember that most `jl_...` functions -can sometimes invoke garbage collection. +It is in general impossible to predict which values will be old at runtime, so the write +barrier must be inserted after all explicit stores. One notable exception is if the `parent` +object has just been allocated and no garbage collection has run since then. Note that most +`jl_...` functions can sometimes invoke garbage collection. The write barrier is also necessary for arrays of pointers when updating their data directly. For example: @@ -434,7 +439,7 @@ data[0] = some_value; jl_gc_wb(some_array, some_value); ``` -### Manipulating the Garbage Collector +### Controlling the Garbage Collector There are some functions to control the GC. In normal use cases, these should not be necessary. @@ -456,8 +461,8 @@ struct that contains: * A pointer to the data block * Information about the sizes of the array -To keep things simple, we start with a 1D array. Creating an array containing Float64 elements -of length 10 is done by: +To keep things simple, we start with a 1D array. Creating an array containing Float64 +elements of length 10 can be done like this: ```c jl_value_t* array_type = jl_apply_array_type((jl_value_t*)jl_float64_type, 1); @@ -476,7 +481,7 @@ The last argument is a boolean indicating whether Julia should take ownership of this argument is non-zero, the GC will call `free` on the data pointer when the array is no longer referenced. -In order to access the data of x, we can use `jl_array_data`: +In order to access the data of `x`, we can use `jl_array_data`: ```c double *xData = (double*)jl_array_data(x); @@ -584,6 +589,7 @@ jl_errorf("argument x = %d is too large", x); where in this example `x` is assumed to be an integer. + ### Thread-safety In general, the Julia C API is not fully thread-safe. When embedding Julia in a multi-threaded application care needs to be taken not to violate From 180ff2617dfcaf9ab3b7d58fc96da7bcfc2d99e7 Mon Sep 17 00:00:00 2001 From: Michael Abbott <32575566+mcabbott@users.noreply.github.com> Date: Thu, 12 May 2022 01:46:06 -0400 Subject: [PATCH 28/38] minimal fix of 41221 (#43723) Co-authored-by: Kristoffer Carlsson --- base/subarray.jl | 6 +++++- test/subarray.jl | 13 +++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/base/subarray.jl b/base/subarray.jl index ff2408bb48534..17bd6450f0d79 100644 --- a/base/subarray.jl +++ b/base/subarray.jl @@ -171,10 +171,14 @@ julia> view(2:5, 2:3) # returns a range as type is immutable 3:4 ``` """ -function view(A::AbstractArray, I::Vararg{Any,N}) where {N} +function view(A::AbstractArray{<:Any,N}, I::Vararg{Any,M}) where {N,M} @inline J = map(i->unalias(A,i), to_indices(A, I)) @boundscheck checkbounds(A, J...) + if length(J) > ndims(A) && J[N+1:end] isa Tuple{Vararg{Int}} + # view([1,2,3], :, 1) does not need to reshape + return unsafe_view(A, J[1:N]...) + end unsafe_view(_maybe_reshape_parent(A, index_ndims(J...)), J...) end diff --git a/test/subarray.jl b/test/subarray.jl index cc8aab94e4c42..98335cb257110 100644 --- a/test/subarray.jl +++ b/test/subarray.jl @@ -737,3 +737,16 @@ end end end end + +@testset "issue #41221: view(::Vector, :, 1)" begin + v = randn(3) + @test view(v,:,1) == v + @test parent(view(v,:,1)) === v + @test parent(view(v,2:3,1,1)) === v + @test_throws BoundsError view(v,:,2) + @test_throws BoundsError view(v,:,1,2) + + m = randn(4,5).+im + @test view(m, 1:2, 3, 1, 1) == m[1:2, 3] + @test parent(view(m, 1:2, 3, 1, 1)) === m +end From bc623e51b659d304dfeda3680f938fbbf97f439a Mon Sep 17 00:00:00 2001 From: t-bltg Date: Thu, 12 May 2022 07:46:41 +0200 Subject: [PATCH 29/38] LibGit2: expose trace_set (#43439) Co-authored-by: Kristoffer Carlsson --- stdlib/LibGit2/docs/src/index.md | 2 +- stdlib/LibGit2/src/LibGit2.jl | 9 +++++++++ stdlib/LibGit2/src/callbacks.jl | 7 +++++++ stdlib/LibGit2/src/consts.jl | 12 +++++++++++- stdlib/LibGit2/test/libgit2.jl | 6 ++++++ 5 files changed, 34 insertions(+), 2 deletions(-) diff --git a/stdlib/LibGit2/docs/src/index.md b/stdlib/LibGit2/docs/src/index.md index 4024bc6f28f0f..3205c4c5d6987 100644 --- a/stdlib/LibGit2/docs/src/index.md +++ b/stdlib/LibGit2/docs/src/index.md @@ -9,7 +9,7 @@ It is expected that this module will eventually be moved into a separate package Some of this documentation assumes some prior knowledge of the libgit2 API. For more information on some of the objects and methods referenced here, consult the upstream -[libgit2 API reference](https://libgit2.org/libgit2/#v0.25.1). +[libgit2 API reference](https://libgit2.org/libgit2/#v1.0.0). ```@docs LibGit2.Buffer diff --git a/stdlib/LibGit2/src/LibGit2.jl b/stdlib/LibGit2/src/LibGit2.jl index 5970ae19359bf..ece246864e51f 100644 --- a/stdlib/LibGit2/src/LibGit2.jl +++ b/stdlib/LibGit2/src/LibGit2.jl @@ -1023,4 +1023,13 @@ function set_ssl_cert_locations(cert_loc) throw(Error.GitError(err.class, err.code, chomp(msg))) end +""" + trace_set(level::Union{Integer,GIT_TRACE_LEVEL}) + +Sets the system tracing configuration to the specified level. +""" +function trace_set(level::Union{Integer,Consts.GIT_TRACE_LEVEL}, cb=trace_cb()) + @check @ccall "libgit2".git_trace_set(level::Cint, cb::Ptr{Cvoid})::Cint +end + end # module diff --git a/stdlib/LibGit2/src/callbacks.jl b/stdlib/LibGit2/src/callbacks.jl index 6228f442df37f..83ac58010ac32 100644 --- a/stdlib/LibGit2/src/callbacks.jl +++ b/stdlib/LibGit2/src/callbacks.jl @@ -502,6 +502,11 @@ function ssh_knownhost_check( return Consts.LIBSSH2_KNOWNHOST_CHECK_NOTFOUND end +function trace_callback(level::Cint, msg::Cstring)::Cint + println(stderr, "[$level]: $(unsafe_string(msg))") + return 0 +end + "C function pointer for `mirror_callback`" mirror_cb() = @cfunction(mirror_callback, Cint, (Ptr{Ptr{Cvoid}}, Ptr{Cvoid}, Cstring, Cstring, Ptr{Cvoid})) "C function pointer for `credentials_callback`" @@ -510,3 +515,5 @@ credentials_cb() = @cfunction(credentials_callback, Cint, (Ptr{Ptr{Cvoid}}, Cstr fetchhead_foreach_cb() = @cfunction(fetchhead_foreach_callback, Cint, (Cstring, Cstring, Ptr{GitHash}, Cuint, Any)) "C function pointer for `certificate_callback`" certificate_cb() = @cfunction(certificate_callback, Cint, (Ptr{CertHostKey}, Cint, Ptr{Cchar}, Ptr{Cvoid})) +"C function pointer for `trace_callback`" +trace_cb() = @cfunction(trace_callback, Cint, (Cint, Cstring)) diff --git a/stdlib/LibGit2/src/consts.jl b/stdlib/LibGit2/src/consts.jl index 2bc9edaf8950b..dfe327935e3b7 100644 --- a/stdlib/LibGit2/src/consts.jl +++ b/stdlib/LibGit2/src/consts.jl @@ -441,7 +441,6 @@ These are used to select which global option to set or get and are used in `git_ SET_TEMPLATE_PATH = 11, SET_SSL_CERT_LOCATIONS = 12) - """ Option flags for `GitProxy`. @@ -453,4 +452,15 @@ Option flags for `GitProxy`. PROXY_AUTO, PROXY_SPECIFIED) +# Available tracing levels. +@enum GIT_TRACE_LEVEL begin + TRACE_NONE + TRACE_FATAL + TRACE_ERROR + TRACE_WARN + TRACE_INFO + TRACE_DEBUG + TRACE_TRACE +end + end diff --git a/stdlib/LibGit2/test/libgit2.jl b/stdlib/LibGit2/test/libgit2.jl index fb1bd2fa1e850..af140fe97f6d1 100644 --- a/stdlib/LibGit2/test/libgit2.jl +++ b/stdlib/LibGit2/test/libgit2.jl @@ -220,6 +220,12 @@ end end end +@testset "Trace" begin + code = "import LibGit2; LibGit2.trace_set(LibGit2.Consts.TRACE_DEBUG); exit(LibGit2.trace_set(0))" + p = run(`$(Base.julia_cmd()) --startup-file=no -e $code`, wait=false); wait(p) + @test success(p) +end + # See #21872 and #21636 LibGit2.version() >= v"0.26.0" && Sys.isunix() && @testset "Default config with symlink" begin with_libgit2_temp_home() do tmphome From c889fbc5260f63e1634ae81ad870a60a55522e72 Mon Sep 17 00:00:00 2001 From: Jerry Ling Date: Thu, 12 May 2022 01:47:21 -0400 Subject: [PATCH 30/38] allow CartesianIndex in `isassigned` (#43394) Co-authored-by: Jeff Bezanson Co-authored-by: N5N3 <2642243996@qq.com> Co-authored-by: Kristoffer Carlsson --- base/multidimensional.jl | 3 +++ test/abstractarray.jl | 4 ++-- test/cartesian.jl | 9 +++++++++ 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/base/multidimensional.jl b/base/multidimensional.jl index b5e401a7834e7..85d9380c076b4 100644 --- a/base/multidimensional.jl +++ b/base/multidimensional.jl @@ -1546,6 +1546,9 @@ end end end +isassigned(a::AbstractArray, i::CartesianIndex) = isassigned(a, Tuple(i)...) +isassigned(a::AbstractArray, i::Union{Integer, CartesianIndex}...) = isassigned(a, CartesianIndex(i)) + ## permutedims ## Permute array dims ## diff --git a/test/abstractarray.jl b/test/abstractarray.jl index df2dbe1c198b9..a9236ecf5d5be 100644 --- a/test/abstractarray.jl +++ b/test/abstractarray.jl @@ -494,9 +494,9 @@ function test_primitives(::Type{T}, shape, ::Type{TestAbstractArray}) where T # isassigned(a::AbstractArray, i::Int...) j = rand(1:length(B)) - @test isassigned(B, j) == true + @test isassigned(B, j) if T == T24Linear - @test isassigned(B, length(B) + 1) == false + @test !isassigned(B, length(B) + 1) end # reshape(a::AbstractArray, dims::Dims) diff --git a/test/cartesian.jl b/test/cartesian.jl index b3cb8315decad..8d7fb0b25ba57 100644 --- a/test/cartesian.jl +++ b/test/cartesian.jl @@ -521,3 +521,12 @@ f39705() = Base.Cartesian.@nany 0 _ -> true @test @inferred(CartesianIndices((true, false))) == CartesianIndices((1, 0)) @test @inferred(CartesianIndices((false, true))) == CartesianIndices((0, 1)) end + +@testset "CartedianIndex isassigned" begin + A = rand(2, 3, 3) + @test isassigned(A, CartesianIndex(1, 2, 3)) + @test !isassigned(A, CartesianIndex(1, 2, 5)) + @test isassigned(A, 1, CartesianIndex(2, 3)) + @test isassigned(A, CartesianIndex(1, 2), 3) + @test !isassigned(A, CartesianIndex(5, 2), 3) +end From 89f16e5830dfef229825c47d33f6b440a1ade13c Mon Sep 17 00:00:00 2001 From: Fred Callaway Date: Thu, 12 May 2022 01:47:57 -0400 Subject: [PATCH 31/38] Allow constructing WorkerPool from AbstractRange{Int} (#44376) Co-authored-by: Kristoffer Carlsson --- stdlib/Distributed/src/workerpool.jl | 9 ++++++--- stdlib/Distributed/test/distributed_exec.jl | 3 ++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/stdlib/Distributed/src/workerpool.jl b/stdlib/Distributed/src/workerpool.jl index 354c61c845113..0cada2db103de 100644 --- a/stdlib/Distributed/src/workerpool.jl +++ b/stdlib/Distributed/src/workerpool.jl @@ -33,9 +33,9 @@ function WorkerPool() end """ - WorkerPool(workers::Vector{Int}) + WorkerPool(workers::Union{Vector{Int},AbstractRange{Int}}) -Create a `WorkerPool` from a vector of worker ids. +Create a `WorkerPool` from a vector or range of worker ids. # Examples ```julia-repl @@ -43,9 +43,12 @@ Create a `WorkerPool` from a vector of worker ids. julia> WorkerPool([2, 3]) WorkerPool(Channel{Int64}(sz_max:9223372036854775807,sz_curr:2), Set([2, 3]), RemoteChannel{Channel{Any}}(1, 1, 6)) + +julia> WorkerPool(2:4) +WorkerPool(Channel{Int64}(sz_max:9223372036854775807,sz_curr:2), Set([4, 2, 3]), RemoteChannel{Channel{Any}}(1, 1, 7)) ``` """ -function WorkerPool(workers::Vector{Int}) +function WorkerPool(workers::Union{Vector{Int},AbstractRange{Int}}) pool = WorkerPool() foreach(w->push!(pool, w), workers) return pool diff --git a/stdlib/Distributed/test/distributed_exec.jl b/stdlib/Distributed/test/distributed_exec.jl index 203ea6de66533..dee10220d9997 100644 --- a/stdlib/Distributed/test/distributed_exec.jl +++ b/stdlib/Distributed/test/distributed_exec.jl @@ -665,7 +665,8 @@ pmap(_->myid(), 1:nworkers()) # priming run wp = WorkerPool(workers()) @test nworkers() == length(unique(pmap(_->myid(), wp, 1:100))) @test nworkers() == length(unique(remotecall_fetch(wp->pmap(_->myid(), wp, 1:100), id_other, wp))) - +wp = WorkerPool(2:3) +@test sort(unique(pmap(_->myid(), wp, 1:100))) == [2,3] # CachingPool tests wp = CachingPool(workers()) From c78cd6632824abfe940d4e2cb3e95ec61e141628 Mon Sep 17 00:00:00 2001 From: Jakob Nybo Nissen Date: Thu, 12 May 2022 07:55:17 +0200 Subject: [PATCH 32/38] Restrict Regex signatures from SubString to SubString{String} (#45281) By itself, SubString is agnostic about the layout of its underlying string. Julia's Regex library assumes a UTF-8 encoded, heap-allocated string type, a criterion fulfilled by SubString{String}, but not SubString. --- base/regex.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/base/regex.jl b/base/regex.jl index b7e1909ece21e..7a69ecbf7cdbd 100644 --- a/base/regex.jl +++ b/base/regex.jl @@ -269,7 +269,7 @@ function occursin(r::Regex, s::AbstractString; offset::Integer=0) return PCRE.exec_r(r.regex, String(s), offset, r.match_options) end -function occursin(r::Regex, s::SubString; offset::Integer=0) +function occursin(r::Regex, s::SubString{String}; offset::Integer=0) compile(r) return PCRE.exec_r(r.regex, s, offset, r.match_options) end @@ -301,7 +301,7 @@ function startswith(s::AbstractString, r::Regex) return PCRE.exec_r(r.regex, String(s), 0, r.match_options | PCRE.ANCHORED) end -function startswith(s::SubString, r::Regex) +function startswith(s::SubString{String}, r::Regex) compile(r) return PCRE.exec_r(r.regex, s, 0, r.match_options | PCRE.ANCHORED) end @@ -333,7 +333,7 @@ function endswith(s::AbstractString, r::Regex) return PCRE.exec_r(r.regex, String(s), 0, r.match_options | PCRE.ENDANCHORED) end -function endswith(s::SubString, r::Regex) +function endswith(s::SubString{String}, r::Regex) compile(r) return PCRE.exec_r(r.regex, s, 0, r.match_options | PCRE.ENDANCHORED) end From b605679df99783c849a32dd994b9954fb8c444f3 Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Thu, 12 May 2022 07:56:09 +0200 Subject: [PATCH 33/38] Document and export `contractuser` (#45279) Co-authored-by: Elias Kuthe --- base/exports.jl | 1 + base/path.jl | 4 ++++ doc/src/base/file.md | 1 + stdlib/Logging/src/ConsoleLogger.jl | 2 +- test/path.jl | 8 ++++---- 5 files changed, 11 insertions(+), 5 deletions(-) diff --git a/base/exports.jl b/base/exports.jl index a8c8ff8dcda33..4cc2ec011aa89 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -882,6 +882,7 @@ export basename, dirname, expanduser, + contractuser, homedir, isabspath, isdirpath, diff --git a/base/path.jl b/base/path.jl index 454fe5bd65d32..dea1a1e3eef9d 100644 --- a/base/path.jl +++ b/base/path.jl @@ -516,6 +516,8 @@ end expanduser(path::AbstractString) -> AbstractString On Unix systems, replace a tilde character at the start of a path with the current user's home directory. + +See also: [`contractuser`](@ref). """ expanduser(path::AbstractString) @@ -523,6 +525,8 @@ expanduser(path::AbstractString) contractuser(path::AbstractString) -> AbstractString On Unix systems, if the path starts with `homedir()`, replace it with a tilde character. + +See also: [`expanduser`](@ref). """ contractuser(path::AbstractString) diff --git a/doc/src/base/file.md b/doc/src/base/file.md index 86a1f2bab5dcd..5e4f34ba510ab 100644 --- a/doc/src/base/file.md +++ b/doc/src/base/file.md @@ -58,6 +58,7 @@ Base.Filesystem.normpath Base.Filesystem.realpath Base.Filesystem.relpath Base.Filesystem.expanduser +Base.Filesystem.contractuser Base.Filesystem.splitdir Base.Filesystem.splitdrive Base.Filesystem.splitext diff --git a/stdlib/Logging/src/ConsoleLogger.jl b/stdlib/Logging/src/ConsoleLogger.jl index 86e3d587eb452..bb040aac91858 100644 --- a/stdlib/Logging/src/ConsoleLogger.jl +++ b/stdlib/Logging/src/ConsoleLogger.jl @@ -73,7 +73,7 @@ function default_metafmt(level::LogLevel, _module, group, id, file, line) _module !== nothing && (suffix *= string(_module)::String) if file !== nothing _module !== nothing && (suffix *= " ") - suffix *= Base.contractuser(file)::String + suffix *= contractuser(file)::String if line !== nothing suffix *= ":$(isa(line, UnitRange) ? "$(first(line))-$(last(line))" : line)" end diff --git a/test/path.jl b/test/path.jl index 31de4baffd1a0..4a4caa6b0b115 100644 --- a/test/path.jl +++ b/test/path.jl @@ -34,11 +34,11 @@ @test expanduser(S("x")) == "x" @test expanduser(S("~")) == (Sys.iswindows() ? "~" : homedir()) end - @testset "Base.contractuser" begin - @test Base.contractuser(S(homedir())) == (Sys.iswindows() ? homedir() : "~") - @test Base.contractuser(S(joinpath(homedir(), "x"))) == + @testset "contractuser" begin + @test contractuser(S(homedir())) == (Sys.iswindows() ? homedir() : "~") + @test contractuser(S(joinpath(homedir(), "x"))) == (Sys.iswindows() ? joinpath(homedir(), "x") : "~$(sep)x") - @test Base.contractuser(S("/foo/bar")) == "/foo/bar" + @test contractuser(S("/foo/bar")) == "/foo/bar" end @testset "isdirpath" begin @test !isdirpath(S("foo")) From cd51a3930012e0486ec3683ecfe7ad78f14dc46f Mon Sep 17 00:00:00 2001 From: Katharine Hyatt Date: Thu, 12 May 2022 01:57:03 -0400 Subject: [PATCH 34/38] Test for MethodError for gcd/lcm/gcdx (#45250) --- test/intfuncs.jl | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/test/intfuncs.jl b/test/intfuncs.jl index cf7ae89ea1dd7..2bbdfb0aed7af 100644 --- a/test/intfuncs.jl +++ b/test/intfuncs.jl @@ -211,6 +211,14 @@ end @test gcd(MyRational(2//3), 3) == gcd(2//3, 3) == gcd(Real[MyRational(2//3), 3]) @test lcm(MyRational(2//3), 3) == lcm(2//3, 3) == lcm(Real[MyRational(2//3), 3]) @test gcdx(MyRational(2//3), 3) == gcdx(2//3, 3) + + # test error path + struct MyOtherRational <: Real + val::Rational{Int} + end + @test_throws MethodError gcd(MyOtherRational(2//3), MyOtherRational(3//4)) + @test_throws MethodError lcm(MyOtherRational(2//3), MyOtherRational(3//4)) + @test_throws MethodError gcdx(MyOtherRational(2//3), MyOtherRational(3//4)) end @testset "invmod" begin From 3653d3d05ffb8a652b12ff22e550ec8fe07edd1d Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Thu, 12 May 2022 07:58:41 +0200 Subject: [PATCH 35/38] fix keyword values being shown in `MethodError` (#45255) --- base/errorshow.jl | 3 +-- test/errorshow.jl | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/base/errorshow.jl b/base/errorshow.jl index 2f6fa6604b775..4ae02885aa618 100644 --- a/base/errorshow.jl +++ b/base/errorshow.jl @@ -272,8 +272,7 @@ function showerror(io::IO, ex::MethodError) if !isempty(kwargs) print(io, "; ") for (i, (k, v)) in enumerate(kwargs) - print(io, k, "=") - show(IOContext(io, :limit => true), v) + print(io, k, "::", typeof(v)) i == length(kwargs)::Int || print(io, ", ") end end diff --git a/test/errorshow.jl b/test/errorshow.jl index 72a2ebb1e9cbe..f2d21168f4d19 100644 --- a/test/errorshow.jl +++ b/test/errorshow.jl @@ -175,7 +175,7 @@ let no_kwsorter_match, e no_kwsorter_match() = 0 no_kwsorter_match(a;y=1) = y e = try no_kwsorter_match(y=1) catch ex; ex; end - @test occursin(r"no method matching.+\(; y=1\)", sprint(showerror, e)) + @test occursin(Regex("no method matching.+\\(; y::$(Int)\\)"), sprint(showerror, e)) end ac15639line = @__LINE__ @@ -623,7 +623,7 @@ let err_str @test occursin(r"MethodError: no method matching one\(::.*HasNoOne\)", err_str) @test occursin("HasNoOne does not support `one`; did you mean `oneunit`?", err_str) err_str = @except_str one(HasNoOne(); value=2) MethodError - @test occursin(r"MethodError: no method matching one\(::.*HasNoOne; value=2\)", err_str) + @test occursin(Regex("MethodError: no method matching one\\(::.*HasNoOne; value::$(Int)\\)"), err_str) @test occursin("`one` doesn't take keyword arguments, that would be silly", err_str) end pop!(Base.Experimental._hint_handlers[MethodError]) # order is undefined, don't copy this From 6f4ce97c72f9af5f8c8707ffffe9dbff0e8d9324 Mon Sep 17 00:00:00 2001 From: Rafael Fourquet Date: Thu, 12 May 2022 13:50:52 +0200 Subject: [PATCH 36/38] REPL: allow editing current input in editor (via Meta-e) (#33759) --- NEWS.md | 3 +++ stdlib/REPL/docs/src/index.md | 7 +++--- stdlib/REPL/src/LineEdit.jl | 46 +++++++++++++++++++++++++++++++++++ stdlib/REPL/src/REPL.jl | 11 ++------- stdlib/REPL/test/repl.jl | 15 ++++++++++++ 5 files changed, 70 insertions(+), 12 deletions(-) diff --git a/NEWS.md b/NEWS.md index 36660f0078c76..4c3f43d7a31e0 100644 --- a/NEWS.md +++ b/NEWS.md @@ -87,6 +87,9 @@ Standard library changes #### REPL +* `Meta-e` now opens the current input in an editor. The content (if modified) will be + executed upon existing the editor. + #### SparseArrays #### Dates diff --git a/stdlib/REPL/docs/src/index.md b/stdlib/REPL/docs/src/index.md index 4120f0ae4c6c3..1d98bb829f785 100644 --- a/stdlib/REPL/docs/src/index.md +++ b/stdlib/REPL/docs/src/index.md @@ -270,9 +270,10 @@ to do so), or pressing Esc and then the key. | `meta-l` | Change the next word to lowercase | | `^/`, `^_` | Undo previous editing action | | `^Q` | Write a number in REPL and press `^Q` to open editor at corresponding stackframe or method | -| `meta-Left Arrow` | indent the current line on the left | -| `meta-Right Arrow` | indent the current line on the right | -| `meta-.` | insert last word from previous history entry | +| `meta-Left Arrow` | Indent the current line on the left | +| `meta-Right Arrow` | Indent the current line on the right | +| `meta-.` | Insert last word from previous history entry | +| `meta-e` | Edit the current input in an editor | ### Customizing keybindings diff --git a/stdlib/REPL/src/LineEdit.jl b/stdlib/REPL/src/LineEdit.jl index 89f57383d5e48..9fa0b4932815b 100644 --- a/stdlib/REPL/src/LineEdit.jl +++ b/stdlib/REPL/src/LineEdit.jl @@ -12,6 +12,8 @@ import ..Terminals: raw!, width, height, cmove, getX, import Base: ensureroom, show, AnyDict, position using Base: something +using InteractiveUtils: InteractiveUtils + abstract type TextInterface end # see interface immediately below abstract type ModeState end # see interface below abstract type HistoryProvider end @@ -1295,6 +1297,49 @@ _edit_indent(buf::IOBuffer, b::Int, num::Int) = num >= 0 ? edit_splice!(buf, b => b, ' '^num, rigid_mark=false) : edit_splice!(buf, b => (b - num)) +function mode_idx(hist::HistoryProvider, mode::TextInterface) + c = :julia + for (k,v) in hist.mode_mapping + isequal(v, mode) && (c = k) + end + return c +end + +function guess_current_mode_name(s) + try + mode_idx(s.current_mode.hist, s.current_mode) + catch + nothing + end +end + +# edit current input in editor +function edit_input(s, f = (filename, line) -> InteractiveUtils.edit(filename, line)) + mode_name = guess_current_mode_name(s) + filename = tempname() + if mode_name == :julia + filename *= ".jl" + elseif mode_name == :shell + filename *= ".sh" + end + buf = buffer(s) + pos = position(buf) + str = String(take!(buf)) + line = 1 + count(==(_newline), view(str, 1:pos)) + write(filename, str) + f(filename, line) + str_mod = readchomp(filename) + rm(filename) + if str != str_mod # something was changed, run the input + write(buf, str_mod) + commit_line(s) + :done + else # no change, the edit session probably unsuccessful + write(buf, str) + seek(buf, pos) # restore state from before edit + refresh_line(s) + end +end history_prev(::EmptyHistoryProvider) = ("", false) history_next(::EmptyHistoryProvider) = ("", false) @@ -2337,6 +2382,7 @@ AnyDict( "\eu" => (s::MIState,o...)->edit_upper_case(s), "\el" => (s::MIState,o...)->edit_lower_case(s), "\ec" => (s::MIState,o...)->edit_title_case(s), + "\ee" => (s::MIState,o...) -> edit_input(s), ) const history_keymap = AnyDict( diff --git a/stdlib/REPL/src/REPL.jl b/stdlib/REPL/src/REPL.jl index 17837a24e3195..df35c433d9f78 100644 --- a/stdlib/REPL/src/REPL.jl +++ b/stdlib/REPL/src/REPL.jl @@ -59,7 +59,8 @@ import ..LineEdit: terminal, MIState, PromptState, - TextInterface + TextInterface, + mode_idx include("REPLCompletions.jl") using .REPLCompletions @@ -601,14 +602,6 @@ function hist_from_file(hp::REPLHistoryProvider, path::String) return hp end -function mode_idx(hist::REPLHistoryProvider, mode::TextInterface) - c = :julia - for (k,v) in hist.mode_mapping - isequal(v, mode) && (c = k) - end - return c -end - function add_history(hist::REPLHistoryProvider, s::PromptState) str = rstrip(String(take!(copy(s.input_buffer)))) isempty(strip(str)) && return diff --git a/stdlib/REPL/test/repl.jl b/stdlib/REPL/test/repl.jl index 97d9e864f1fe0..9adde37e95b10 100644 --- a/stdlib/REPL/test/repl.jl +++ b/stdlib/REPL/test/repl.jl @@ -1499,3 +1499,18 @@ for prompt = ["TestΠ", () -> randstring(rand(1:10))] @test buffercontents(LineEdit.buffer(s)) == "xyz = 2" end end + +fake_repl() do stdin_write, stdout_read, repl + repltask = @async begin + REPL.run_repl(repl) + end + repl.interface = REPL.setup_interface(repl) + s = LineEdit.init_state(repl.t, repl.interface) + LineEdit.edit_insert(s, "1234") + @show buffercontents(LineEdit.buffer(s)) + input_f = function(filename, line) + write(filename, "123456\n") + end + LineEdit.edit_input(s, input_f) + @test buffercontents(LineEdit.buffer(s)) == "123456" +end From c65e56f0279b8ea48556cbc1726fd78937930be4 Mon Sep 17 00:00:00 2001 From: Morten Piibeleht Date: Fri, 13 May 2022 01:50:59 +1200 Subject: [PATCH 37/38] Change PDF cover font to DejaVu Sans (#45290) --- doc/src/assets/custom.sty | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/doc/src/assets/custom.sty b/doc/src/assets/custom.sty index f257d2d3d2174..03e6ff805cd3f 100644 --- a/doc/src/assets/custom.sty +++ b/doc/src/assets/custom.sty @@ -40,6 +40,7 @@ contents={ }}% % ---- Heading font style -\DeclareFixedFont{\MainHeading}{T1}{phv}{b}{n}{1.5cm} -\DeclareFixedFont{\SecondaryHeading}{T1}{phv}{b}{n}{0.8cm} +\usepackage{anyfontsize} +\newcommand{\MainHeading}{\fontspec{DejaVu Sans}\fontsize{40}{40}\selectfont\bfseries} +\newcommand{\SecondaryHeading}{\fontspec{DejaVu Sans}\LARGE} %% cover page END ------------------------------------------------------------- From a74ef572f9b5347b050e0ba919c50e4cb8048d8d Mon Sep 17 00:00:00 2001 From: DilumAluthgeBot <43731525+DilumAluthgeBot@users.noreply.github.com> Date: Thu, 12 May 2022 11:14:40 -0400 Subject: [PATCH 38/38] =?UTF-8?q?=F0=9F=A4=96=20Bump=20the=20Pkg=20stdlib?= =?UTF-8?q?=20from=2054d5c9e5=20to=20dd7fbb2b=20(#45288)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Dilum Aluthge --- .../Pkg-54d5c9e5175e94a05d6c9c9e54ad5b42d068eb17.tar.gz/md5 | 1 - .../Pkg-54d5c9e5175e94a05d6c9c9e54ad5b42d068eb17.tar.gz/sha512 | 1 - .../Pkg-dd7fbb2b50b1cd6b6812969350f9178ee9c93363.tar.gz/md5 | 1 + .../Pkg-dd7fbb2b50b1cd6b6812969350f9178ee9c93363.tar.gz/sha512 | 1 + stdlib/Pkg.version | 2 +- 5 files changed, 3 insertions(+), 3 deletions(-) delete mode 100644 deps/checksums/Pkg-54d5c9e5175e94a05d6c9c9e54ad5b42d068eb17.tar.gz/md5 delete mode 100644 deps/checksums/Pkg-54d5c9e5175e94a05d6c9c9e54ad5b42d068eb17.tar.gz/sha512 create mode 100644 deps/checksums/Pkg-dd7fbb2b50b1cd6b6812969350f9178ee9c93363.tar.gz/md5 create mode 100644 deps/checksums/Pkg-dd7fbb2b50b1cd6b6812969350f9178ee9c93363.tar.gz/sha512 diff --git a/deps/checksums/Pkg-54d5c9e5175e94a05d6c9c9e54ad5b42d068eb17.tar.gz/md5 b/deps/checksums/Pkg-54d5c9e5175e94a05d6c9c9e54ad5b42d068eb17.tar.gz/md5 deleted file mode 100644 index fa7ad439f14b0..0000000000000 --- a/deps/checksums/Pkg-54d5c9e5175e94a05d6c9c9e54ad5b42d068eb17.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -6cd9a8d83b45b88b2ba5c43ccd929d00 diff --git a/deps/checksums/Pkg-54d5c9e5175e94a05d6c9c9e54ad5b42d068eb17.tar.gz/sha512 b/deps/checksums/Pkg-54d5c9e5175e94a05d6c9c9e54ad5b42d068eb17.tar.gz/sha512 deleted file mode 100644 index 4e2ce708ba22f..0000000000000 --- a/deps/checksums/Pkg-54d5c9e5175e94a05d6c9c9e54ad5b42d068eb17.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -2e2c626103a8653c5e3f29cc2460c2e703ef2277c597d835fb58ee0d1ddb1ef535b82e7e949e7a9d83bfa5adc534d2a6cc92d38a444c91d3df094bd9258fb3e6 diff --git a/deps/checksums/Pkg-dd7fbb2b50b1cd6b6812969350f9178ee9c93363.tar.gz/md5 b/deps/checksums/Pkg-dd7fbb2b50b1cd6b6812969350f9178ee9c93363.tar.gz/md5 new file mode 100644 index 0000000000000..666c115755092 --- /dev/null +++ b/deps/checksums/Pkg-dd7fbb2b50b1cd6b6812969350f9178ee9c93363.tar.gz/md5 @@ -0,0 +1 @@ +7e0f33cc364bb5ecca768d4460979fee diff --git a/deps/checksums/Pkg-dd7fbb2b50b1cd6b6812969350f9178ee9c93363.tar.gz/sha512 b/deps/checksums/Pkg-dd7fbb2b50b1cd6b6812969350f9178ee9c93363.tar.gz/sha512 new file mode 100644 index 0000000000000..ba5764837c3b8 --- /dev/null +++ b/deps/checksums/Pkg-dd7fbb2b50b1cd6b6812969350f9178ee9c93363.tar.gz/sha512 @@ -0,0 +1 @@ +67195aedff718b08480baf3d96520216640a8d2ae9e8ad4e529b0337f339eea5a87722a15545e2536986edc62e806a8ab585ed9124d9f511d6095a74c9570fba diff --git a/stdlib/Pkg.version b/stdlib/Pkg.version index 1b6a3fa3d0d5d..e0f3f9837eee5 100644 --- a/stdlib/Pkg.version +++ b/stdlib/Pkg.version @@ -1,4 +1,4 @@ PKG_BRANCH = master -PKG_SHA1 = 54d5c9e5175e94a05d6c9c9e54ad5b42d068eb17 +PKG_SHA1 = dd7fbb2b50b1cd6b6812969350f9178ee9c93363 PKG_GIT_URL := https://github.com/JuliaLang/Pkg.jl.git PKG_TAR_URL = https://api.github.com/repos/JuliaLang/Pkg.jl/tarball/$1