Skip to content

Commit

Permalink
Backports release 1.11 (#53790)
Browse files Browse the repository at this point in the history
Backported PRs:
- [x] #53757 <!-- Add an IndexStyle example to the diagind docstring -->
- [x] #53809 <!-- Add missing GC_POP() in emit_cfunction -->
- [x] #53789 <!-- also check that UUID of project is non-null when
treating it as a package -->
- [x] #53805 <!-- precompilepkgs: simplify custom config printing if
only one -->
- [x] #53822 <!-- Bump libuv -->
- [x] #53837 <!-- update MPFR to 4.2.1 -->
- [x] #53862 <!-- precompilepkgs: fix error reporting -->
- [x] #53774 <!-- Remove some duplicates from emitted compilation traces
-->
- [ ] #53696 <!-- add invokelatest to on_done callback in bracketed
paste -->
- [x] #53383 <!-- Add `_unsetindex!` methods for `SubArray`s and
`CartesianIndex`es -->
- [x] #53475 <!-- Fix boundscheck in unsetindex for SubArrays -->
- [x] #53888 
- [x] #53870 <!-- Revert change to checksum for llvm-julia -->
- [x] #53906 <!-- Add `Base.isrelocatable(pkg)` -->
- [x] #53833 <!-- Profile: make heap snapshots viewable in vscode viewer
-->
- [x] #53961 <!-- `LazyString` in `LinearAlgebra.checksquare` error
message -->
- [x] #53962 <!-- Use StringMemory instead of StringVector where
possible -->
- [x] #53825 <!-- profile: doc: update the `Allocs.@profile` doc string
-->
- [x] #53975 <!-- `LazyString` in `DimensionMismatch` error messages in
broadcasting -->
- [x] #53905 <!-- Avoid repeated precompilation when loading from
non-relocatable cachefiles -->
- [x] #53896 <!-- Make reshape and view on Memory produce Arrays and
delete wrap -->
- [x] #53991 <!-- Test and fix non-int-length bug in `view(::Memory,
::Union{UnitRange, Base.OneTo})` -->
  • Loading branch information
KristofferC authored Apr 9, 2024
2 parents 383eb07 + eb96c07 commit 911b8cf
Show file tree
Hide file tree
Showing 50 changed files with 575 additions and 253 deletions.
1 change: 0 additions & 1 deletion NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,6 @@ New library functions
* `Sys.username()` can be used to return the current user's username ([#51897]).
* `Sys.isreadable(), Sys.iswritable()` can be used to check if the current user has access permissions
that permit reading and writing, respectively. ([#53320]).
* `wrap(Array, m::Union{MemoryRef{T}, Memory{T}}, dims)` is the safe counterpart to `unsafe_wrap` ([#52049]).
* `GC.logging_enabled()` can be used to test whether GC logging has been enabled via `GC.enable_logging` ([#51647]).
* `IdSet` is now exported from Base and considered public ([#53262]).
* `@time` now reports a count of any lock conflicts where a `ReentrantLock` had to wait, plus a new macro
Expand Down
15 changes: 14 additions & 1 deletion base/abstractarray.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1473,7 +1473,20 @@ function _setindex!(::IndexCartesian, A::AbstractArray, v, I::Vararg{Int,M}) whe
r
end

_unsetindex!(A::AbstractArray, i::Integer) = _unsetindex!(A, to_index(i))
function _unsetindex!(A::AbstractArray, i::Integer...)
@_propagate_inbounds_meta
_unsetindex!(A, map(to_index, i)...)
end

function _unsetindex!(A::AbstractArray{T}, i::Int...) where T
# this provides a fallback method which is a no-op if the element is already unassigned
# such that copying into an uninitialized object generally always will work,
# even if the specific custom array type has not implemented `_unsetindex!`
@inline
@boundscheck checkbounds(A, i...)
allocatedinline(T) || @inbounds(!isassigned(A, i...)) || throw(MethodError(_unsetindex!, (A, i...)))
return A
end

"""
parent(A)
Expand Down
58 changes: 6 additions & 52 deletions base/array.jl
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,12 @@ function _unsetindex!(A::Array, i::Int)
@inbounds _unsetindex!(GenericMemoryRef(A.ref, i))
return A
end

function _unsetindex!(A::Array, i::Int...)
@inline
@boundscheck checkbounds(A, i...)
@inbounds _unsetindex!(A, _to_linear_index(A, i...))
return A
end

# TODO: deprecate this (aligned_sizeof and/or elsize and/or sizeof(Some{T}) are more correct)
elsize(::Type{A}) where {T,A<:Array{T}} = aligned_sizeof(T)
Expand Down Expand Up @@ -3062,54 +3067,3 @@ intersect(r::AbstractRange, v::AbstractVector) = intersect(v, r)
_getindex(v, i)
end
end

"""
wrap(Array, m::Union{Memory{T}, MemoryRef{T}}, dims)
Create an array of size `dims` using `m` as the underlying memory. This can be thought of as a safe version
of [`unsafe_wrap`](@ref) utilizing `Memory` or `MemoryRef` instead of raw pointers.
"""
function wrap end

# validity checking for _wrap calls, separate from allocation of Array so that it can be more likely to inline into the caller
function _wrap(ref::MemoryRef{T}, dims::NTuple{N, Int}) where {T, N}
mem = ref.mem
mem_len = length(mem) + 1 - memoryrefoffset(ref)
len = Core.checked_dims(dims...)
@boundscheck mem_len >= len || invalid_wrap_err(mem_len, dims, len)
if N != 1 && !(ref === GenericMemoryRef(mem) && len === mem_len)
mem = ccall(:jl_genericmemory_slice, Memory{T}, (Any, Ptr{Cvoid}, Int), mem, ref.ptr_or_offset, len)
ref = MemoryRef(mem)
end
return ref
end

@noinline invalid_wrap_err(len, dims, proddims) = throw(DimensionMismatch(
"Attempted to wrap a MemoryRef of length $len with an Array of size dims=$dims, which is invalid because prod(dims) = $proddims > $len, so that the array would have more elements than the underlying memory can store."))

@eval @propagate_inbounds function wrap(::Type{Array}, m::MemoryRef{T}, dims::NTuple{N, Integer}) where {T, N}
dims = convert(Dims, dims)
ref = _wrap(m, dims)
$(Expr(:new, :(Array{T, N}), :ref, :dims))
end

@eval @propagate_inbounds function wrap(::Type{Array}, m::Memory{T}, dims::NTuple{N, Integer}) where {T, N}
dims = convert(Dims, dims)
ref = _wrap(MemoryRef(m), dims)
$(Expr(:new, :(Array{T, N}), :ref, :dims))
end
@eval @propagate_inbounds function wrap(::Type{Array}, m::MemoryRef{T}, l::Integer) where {T}
dims = (Int(l),)
ref = _wrap(m, dims)
$(Expr(:new, :(Array{T, 1}), :ref, :dims))
end
@eval @propagate_inbounds function wrap(::Type{Array}, m::Memory{T}, l::Integer) where {T}
dims = (Int(l),)
ref = _wrap(MemoryRef(m), (l,))
$(Expr(:new, :(Array{T, 1}), :ref, :dims))
end
@eval @propagate_inbounds function wrap(::Type{Array}, m::Memory{T}) where {T}
ref = MemoryRef(m)
dims = (length(m),)
$(Expr(:new, :(Array{T, 1}), :ref, :dims))
end
6 changes: 3 additions & 3 deletions base/broadcast.jl
Original file line number Diff line number Diff line change
Expand Up @@ -517,8 +517,8 @@ function _bcs(shape::Tuple, newshape::Tuple)
return (_bcs1(shape[1], newshape[1]), _bcs(tail(shape), tail(newshape))...)
end
# _bcs1 handles the logic for a single dimension
_bcs1(a::Integer, b::Integer) = a == 1 ? b : (b == 1 ? a : (a == b ? a : throw(DimensionMismatch("arrays could not be broadcast to a common size; got a dimension with lengths $a and $b"))))
_bcs1(a::Integer, b) = a == 1 ? b : (first(b) == 1 && last(b) == a ? b : throw(DimensionMismatch("arrays could not be broadcast to a common size; got a dimension with lengths $a and $(length(b))")))
_bcs1(a::Integer, b::Integer) = a == 1 ? b : (b == 1 ? a : (a == b ? a : throw(DimensionMismatch(LazyString("arrays could not be broadcast to a common size; got a dimension with lengths ", a, " and ", b)))))
_bcs1(a::Integer, b) = a == 1 ? b : (first(b) == 1 && last(b) == a ? b : throw(DimensionMismatch(LazyString("arrays could not be broadcast to a common size; got a dimension with lengths ", a, " and ", length(b)))))
_bcs1(a, b::Integer) = _bcs1(b, a)
_bcs1(a, b) = _bcsm(b, a) ? axistype(b, a) : _bcsm(a, b) ? axistype(a, b) : throw(DimensionMismatch(LazyString("arrays could not be broadcast to a common size: a has axes ", a, " and b has axes ", b)))
# _bcsm tests whether the second index is consistent with the first
Expand Down Expand Up @@ -1057,7 +1057,7 @@ end


@noinline throwdm(axdest, axsrc) =
throw(DimensionMismatch("destination axes $axdest are not compatible with source axes $axsrc"))
throw(DimensionMismatch(LazyString("destination axes ", axdest, " are not compatible with source axes ", axsrc)))

function restart_copyto_nonleaf!(newdest, dest, bc, val, I, iter, state, count)
# Function barrier that makes the copying to newdest type stable
Expand Down
1 change: 0 additions & 1 deletion base/exports.jl
Original file line number Diff line number Diff line change
Expand Up @@ -460,7 +460,6 @@ export
vcat,
vec,
view,
wrap,
zeros,

# search, find, match and related functions
Expand Down
24 changes: 24 additions & 0 deletions base/genericmemory.jl
Original file line number Diff line number Diff line change
Expand Up @@ -288,3 +288,27 @@ function indcopy(sz::Dims, I::GenericMemory)
src = eltype(I)[I[i][_findin(I[i], i < n ? (1:sz[i]) : (1:s))] for i = 1:n]
dst, src
end

# Wrapping a memory region in an Array
@eval begin # @eval for the Array construction. Block for the docstring.
function reshape(m::GenericMemory{M, T}, dims::Vararg{Int, N}) where {M, T, N}
len = Core.checked_dims(dims...)
length(m) == len || throw(DimensionMismatch("parent has $(length(m)) elements, which is incompatible with size $(dims)"))
ref = MemoryRef(m)
$(Expr(:new, :(Array{T, N}), :ref, :dims))
end

"""
view(m::GenericMemory{M, T}, inds::Union{UnitRange, OneTo})
Create a vector `v::Vector{T}` backed by the specified indices of `m`. It is only safe to
resize `v` if `m` is subseqently not used.
"""
function view(m::GenericMemory{M, T}, inds::Union{UnitRange, OneTo}) where {M, T}
isempty(inds) && return T[] # needed to allow view(Memory{T}(undef, 0), 2:1)
@boundscheck checkbounds(m, inds)
ref = MemoryRef(m, first(inds)) # @inbounds would be safe here but does not help performance.
dims = (Int(length(inds)),)
$(Expr(:new, :(Array{T, 1}), :ref, :dims))
end
end
2 changes: 1 addition & 1 deletion base/gmp.jl
Original file line number Diff line number Diff line change
Expand Up @@ -754,7 +754,7 @@ function string(n::BigInt; base::Integer = 10, pad::Integer = 1)
iszero(n) && pad < 1 && return ""
nd1 = ndigits(n, base=base)
nd = max(nd1, pad)
sv = Base.StringVector(nd + isneg(n))
sv = Base.StringMemory(nd + isneg(n))
GC.@preserve sv MPZ.get_str!(pointer(sv) + nd - nd1, base, n)
@inbounds for i = (1:nd-nd1) .+ isneg(n)
sv[i] = '0' % UInt8
Expand Down
12 changes: 6 additions & 6 deletions base/intfuncs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -742,7 +742,7 @@ ndigits(x::Integer; base::Integer=10, pad::Integer=1) = max(pad, ndigits0z(x, ba
function bin(x::Unsigned, pad::Int, neg::Bool)
m = top_set_bit(x)
n = neg + max(pad, m)
a = StringVector(n)
a = StringMemory(n)
# for i in 0x0:UInt(n-1) # automatic vectorization produces redundant codes
# @inbounds a[n - i] = 0x30 + (((x >> i) % UInt8)::UInt8 & 0x1)
# end
Expand All @@ -769,7 +769,7 @@ end
function oct(x::Unsigned, pad::Int, neg::Bool)
m = div(top_set_bit(x) + 2, 3)
n = neg + max(pad, m)
a = StringVector(n)
a = StringMemory(n)
i = n
while i > neg
@inbounds a[i] = 0x30 + ((x % UInt8)::UInt8 & 0x7)
Expand Down Expand Up @@ -844,7 +844,7 @@ end

function dec(x::Unsigned, pad::Int, neg::Bool)
n = neg + ndigits(x, pad=pad)
a = StringVector(n)
a = StringMemory(n)
append_c_digits_fast(n, x, a, 1)
neg && (@inbounds a[1] = 0x2d) # UInt8('-')
String(a)
Expand All @@ -853,7 +853,7 @@ end
function hex(x::Unsigned, pad::Int, neg::Bool)
m = 2 * sizeof(x) - (leading_zeros(x) >> 2)
n = neg + max(pad, m)
a = StringVector(n)
a = StringMemory(n)
i = n
while i >= 2
b = (x % UInt8)::UInt8
Expand All @@ -880,7 +880,7 @@ function _base(base::Integer, x::Integer, pad::Int, neg::Bool)
b = (base % Int)::Int
digits = abs(b) <= 36 ? base36digits : base62digits
n = neg + ndigits(x, base=b, pad=pad)
a = StringVector(n)
a = StringMemory(n)
i = n
@inbounds while i > neg
if b > 0
Expand Down Expand Up @@ -956,7 +956,7 @@ julia> bitstring(2.2)
function bitstring(x::T) where {T}
isprimitivetype(T) || throw(ArgumentError("$T not a primitive type"))
sz = sizeof(T) * 8
str = StringVector(sz)
str = StringMemory(sz)
i = sz
@inbounds while i >= 4
b = UInt32(sizeof(T) == 1 ? bitcast(UInt8, x) : trunc_int(UInt8, x))
Expand Down
12 changes: 4 additions & 8 deletions base/iobuffer.jl
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ end

# allocate Vector{UInt8}s for IOBuffer storage that can efficiently become Strings
StringMemory(n::Integer) = unsafe_wrap(Memory{UInt8}, _string_n(n))
StringVector(n::Integer) = wrap(Array, StringMemory(n))
StringVector(n::Integer) = view(StringMemory(n), 1:n)::Vector{UInt8}

# IOBuffers behave like Files. They are typically readable and writable. They are seekable. (They can be appendable).

Expand Down Expand Up @@ -456,7 +456,7 @@ function take!(io::IOBuffer)
if nbytes == 0 || io.reinit
data = StringVector(0)
elseif io.writable
data = wrap(Array, MemoryRef(io.data, io.offset + 1), nbytes)
data = view(io.data, io.offset+1:nbytes+io.offset)
else
data = copyto!(StringVector(io.size), 1, io.data, io.offset + 1, nbytes)
end
Expand All @@ -465,7 +465,7 @@ function take!(io::IOBuffer)
if nbytes == 0
data = StringVector(0)
elseif io.writable
data = wrap(Array, MemoryRef(io.data, io.ptr), nbytes)
data = view(io.data, io.ptr:io.ptr+nbytes-1)
else
data = read!(io, data)
end
Expand All @@ -491,11 +491,7 @@ state. This should only be used internally for performance-critical
It might save an allocation compared to `take!` (if the compiler elides the
Array allocation), as well as omits some checks.
"""
_unsafe_take!(io::IOBuffer) =
wrap(Array, io.size == io.offset ?
MemoryRef(Memory{UInt8}()) :
MemoryRef(io.data, io.offset + 1),
io.size - io.offset)
_unsafe_take!(io::IOBuffer) = view(io.data, io.offset+1:io.size)

function write(to::IO, from::GenericIOBuffer)
written::Int = bytesavailable(from)
Expand Down
Loading

0 comments on commit 911b8cf

Please sign in to comment.