• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

JuliaLang / julia / #37767

02 May 2024 04:02AM UTC coverage: 85.708% (-1.7%) from 87.428%
#37767

push

local

web-flow
Pass CodeGenOpt::Less to LLVM at O1 (rather than CodeGenOpt::None). (#37893)

For context, please see
https://github.com/JuliaLang/julia/pull/35086#issuecomment-700944522.
Regarding alignment with clang, please see
https://reviews.llvm.org/D28409
(/https://github.com/JuliaLang/julia/pull/35086#issuecomment-598282810).

```
Prior to Julia 1.5, Julia passed CodeGenOpt::Aggressive to LLVM at -O1.
As of Julia 1.5, Julia passes CodeGenOpt::None to LLVM at -O1.

This reduction in optimization effort at -O1 improved compilation latency,
but induced appreciable runtime regressions.

This commit makes Julia pass CodeGenOpt::Less to LLVM at -O1,
mitigating the runtime regressions caused by CodeGenOpt::None,
while retaining most of CodeGenOpt::None's latency improvements.

This commit also aligns Julia's CodeGenOpt selection at -O1
with that of clang.
```

Best! :)

74571 of 87006 relevant lines covered (85.71%)

14871255.36 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

78.49
/base/subarray.jl
1
# This file is a part of Julia. License is MIT: https://julialang.org/license
2

3
abstract type AbstractCartesianIndex{N} end # This is a hacky forward declaration for CartesianIndex
4
const ViewIndex = Union{Real, AbstractArray}
5
const ScalarIndex = Real
6

7
"""
8
    SubArray{T,N,P,I,L} <: AbstractArray{T,N}
9

10
`N`-dimensional view into a parent array (of type `P`) with an element type `T`, restricted by a tuple of indices (of type `I`). `L` is true for types that support fast linear indexing, and `false` otherwise.
11

12
Construct `SubArray`s using the [`view`](@ref) function.
13
"""
14
struct SubArray{T,N,P,I,L} <: AbstractArray{T,N}
15
    parent::P
16
    indices::I
17
    offset1::Int       # for linear indexing and pointer, only valid when L==true
18
    stride1::Int       # used only for linear indexing
19
    function SubArray{T,N,P,I,L}(parent, indices, offset1, stride1) where {T,N,P,I,L}
20
        @inline
29,973,362✔
21
        check_parent_index_match(parent, indices)
29,973,362✔
22
        new(parent, indices, offset1, stride1)
51,589,602✔
23
    end
24
end
25
# Compute the linear indexability of the indices, and combine it with the linear indexing of the parent
26
function SubArray(parent::AbstractArray, indices::Tuple)
27
    @inline
29,972,793✔
28
    SubArray(IndexStyle(viewindexing(indices), IndexStyle(parent)), parent, ensure_indexable(indices), index_dimsum(indices...))
51,589,047✔
29
end
30
function SubArray(::IndexCartesian, parent::P, indices::I, ::NTuple{N,Any}) where {P,I,N}
31
    @inline
427,359✔
32
    SubArray{eltype(P), N, P, I, false}(parent, indices, 0, 0)
427,374✔
33
end
34
function SubArray(::IndexLinear, parent::P, indices::I, ::NTuple{N,Any}) where {P,I,N}
36✔
35
    @inline
29,545,434✔
36
    # Compute the stride and offset
37
    stride1 = compute_stride1(parent, indices)
29,554,838✔
38
    SubArray{eltype(P), N, P, I, true}(parent, indices, compute_offset1(parent, stride1, indices), stride1)
51,161,673✔
39
end
40

41
check_parent_index_match(parent, indices) = check_parent_index_match(parent, index_ndims(indices...))
29,973,361✔
42
check_parent_index_match(parent::AbstractArray{T,N}, ::NTuple{N, Bool}) where {T,N} = nothing
×
43
check_parent_index_match(parent, ::NTuple{N, Bool}) where {N} =
×
44
    throw(ArgumentError("number of indices ($N) must match the parent dimensionality ($(ndims(parent)))"))
45

46
# This computes the linear indexing compatibility for a given tuple of indices
47
viewindexing(I::Tuple{}) = IndexLinear()
51✔
48
# Leading scalar indices simply increase the stride
49
viewindexing(I::Tuple{ScalarIndex, Vararg{Any}}) = (@inline; viewindexing(tail(I)))
263,760✔
50
# Slices may begin a section which may be followed by any number of Slices
51
viewindexing(I::Tuple{Slice, Slice, Vararg{Any}}) = (@inline; viewindexing(tail(I)))
2,118✔
52
# A UnitRange can follow Slices, but only if all other indices are scalar
53
viewindexing(I::Tuple{Slice, AbstractUnitRange, Vararg{ScalarIndex}}) = IndexLinear()
28,426✔
54
viewindexing(I::Tuple{Slice, Slice, Vararg{ScalarIndex}}) = IndexLinear() # disambiguate
1,393✔
55
# In general, scalar ranges are only fast if all other indices are scalar
56
# Other ranges, such as those of `CartesianIndex`es, are not fast even if these
57
# are followed by `ScalarIndex`es
58
viewindexing(I::Tuple{AbstractRange{<:ScalarIndex}, Vararg{ScalarIndex}}) = IndexLinear()
677,751✔
59
# All other index combinations are slow
60
viewindexing(I::Tuple{Vararg{Any}}) = IndexCartesian()
×
61
# Of course, all other array types are slow
62
viewindexing(I::Tuple{AbstractArray, Vararg{Any}}) = IndexCartesian()
222,971✔
63

64
# Simple utilities
65
size(V::SubArray) = (@inline; map(length, axes(V)))
33,674,425✔
66

67
similar(V::SubArray, T::Type, dims::Dims) = similar(V.parent, T, dims)
24,903✔
68

69
sizeof(V::SubArray) = length(V) * sizeof(eltype(V))
2✔
70
sizeof(V::SubArray{<:Any,<:Any,<:Array}) = length(V) * elsize(V.parent)
54,886✔
71

72
function Base.copy(V::SubArray)
469✔
73
    v = V.parent[V.indices...]
2,197✔
74
    ndims(V) == 0 || return v
1,100✔
75
    x = similar(V) # ensure proper type of x
×
76
    x[] = v
×
77
    return x
×
78
end
79

80
parent(V::SubArray) = V.parent
17,868,941✔
81
parentindices(V::SubArray) = V.indices
2,893✔
82

83
"""
84
    parentindices(A)
85

86
Return the indices in the [`parent`](@ref) which correspond to the view `A`.
87

88
# Examples
89
```jldoctest
90
julia> A = [1 2; 3 4];
91

92
julia> V = view(A, 1, :)
93
2-element view(::Matrix{Int64}, 1, :) with eltype Int64:
94
 1
95
 2
96

97
julia> parentindices(V)
98
(1, Base.Slice(Base.OneTo(2)))
99
```
100
"""
101
function parentindices end
102

103
parentindices(a::AbstractArray) = map(oneto, size(a))
×
104

105
## Aliasing detection
106
dataids(A::SubArray) = (dataids(A.parent)..., _splatmap(dataids, A.indices)...)
3,326,674✔
107
_splatmap(f, ::Tuple{}) = ()
2,549,359✔
108
_splatmap(f, t::Tuple) = (f(t[1])..., _splatmap(f, tail(t))...)
2,586,179✔
109
unaliascopy(A::SubArray) = typeof(A)(unaliascopy(A.parent), map(unaliascopy, A.indices), A.offset1, A.stride1)
4✔
110

111
# When the parent is an Array we can trim the size down a bit. In the future this
112
# could possibly be extended to any mutable array.
113
function unaliascopy(V::SubArray{T,N,A,I,LD}) where {T,N,A<:Array,I<:Tuple{Vararg{Union{ScalarIndex,AbstractRange{<:ScalarIndex},Array{<:Union{ScalarIndex,AbstractCartesianIndex}}}}},LD}
3✔
114
    dest = Array{T}(undef, _trimmedshape(V.indices...))
1,134✔
115
    trimmedpind = _trimmedpind(V.indices...)
567✔
116
    vdest = trimmedpind isa Tuple{Vararg{Union{Slice,Colon}}} ? dest : view(dest, trimmedpind...)
567✔
117
    copyto!(vdest, view(V, _trimmedvind(V.indices...)...))
567✔
118
    indices = map(_trimmedindex, V.indices)
567✔
119
    stride1 = LD ? compute_stride1(dest, indices) : 0
567✔
120
    offset1 = LD ? compute_offset1(dest, stride1, indices) : 0
567✔
121
    SubArray{T,N,A,I,LD}(dest, indices, offset1, stride1)
567✔
122
end
123
# Get the proper trimmed shape
124
_trimmedshape(::ScalarIndex, rest...) = (1, _trimmedshape(rest...)...)
540✔
125
_trimmedshape(i::AbstractRange, rest...) = (isempty(i) ? zero(eltype(i)) : maximum(i), _trimmedshape(rest...)...)
12✔
126
_trimmedshape(i::Union{UnitRange,StepRange,OneTo}, rest...) = (length(i), _trimmedshape(rest...)...)
581✔
127
_trimmedshape(i::AbstractArray{<:ScalarIndex}, rest...) = (length(i), _trimmedshape(rest...)...)
×
128
_trimmedshape(i::AbstractArray{<:AbstractCartesianIndex{0}}, rest...) = _trimmedshape(rest...)
×
129
_trimmedshape(i::AbstractArray{<:AbstractCartesianIndex{N}}, rest...) where {N} = (length(i), ntuple(Returns(1), Val(N - 1))..., _trimmedshape(rest...)...)
×
130
_trimmedshape() = ()
567✔
131
# We can avoid the repeation from `AbstractArray{CartesianIndex{0}}`
132
_trimmedpind(i, rest...) = (map(Returns(:), axes(i))..., _trimmedpind(rest...)...)
540✔
133
_trimmedpind(i::AbstractRange, rest...) = (i, _trimmedpind(rest...)...)
6✔
134
_trimmedpind(i::Union{UnitRange,StepRange,OneTo}, rest...) = ((:), _trimmedpind(rest...)...)
581✔
135
_trimmedpind(i::AbstractArray{<:AbstractCartesianIndex{0}}, rest...) = _trimmedpind(rest...)
×
136
_trimmedpind() = ()
567✔
137
_trimmedvind(i, rest...) = (map(Returns(:), axes(i))..., _trimmedvind(rest...)...)
1,127✔
138
_trimmedvind(i::AbstractArray{<:AbstractCartesianIndex{0}}, rest...) = (map(first, axes(i))..., _trimmedvind(rest...)...)
×
139
_trimmedvind() = ()
567✔
140
# Transform indices to be "dense"
141
_trimmedindex(i::ScalarIndex) = oftype(i, 1)
540✔
142
_trimmedindex(i::AbstractRange) = i
6✔
143
_trimmedindex(i::Union{UnitRange,StepRange,OneTo}) = oftype(i, oneto(length(i)))
581✔
144
_trimmedindex(i::AbstractArray{<:ScalarIndex}) = oftype(i, reshape(eachindex(IndexLinear(), i), axes(i)))
×
145
_trimmedindex(i::AbstractArray{<:AbstractCartesianIndex{0}}) = oftype(i, copy(i))
×
146
function _trimmedindex(i::AbstractArray{<:AbstractCartesianIndex{N}}) where {N}
×
147
    padding = ntuple(Returns(1), Val(N - 1))
×
148
    ax1 = eachindex(IndexLinear(), i)
×
149
    return oftype(i, reshape(CartesianIndices((ax1, padding...)), axes(i)))
×
150
end
151
## SubArray creation
152
# We always assume that the dimensionality of the parent matches the number of
153
# indices that end up getting passed to it, so we store the parent as a
154
# ReshapedArray view if necessary. The trouble is that arrays of `CartesianIndex`
155
# can make the number of effective indices not equal to length(I).
156
_maybe_reshape_parent(A::AbstractArray, ::NTuple{1, Bool}) = reshape(A, Val(1))
33,089✔
157
_maybe_reshape_parent(A::AbstractArray{<:Any,1}, ::NTuple{1, Bool}) = reshape(A, Val(1))
319,245✔
158
_maybe_reshape_parent(A::AbstractArray{<:Any,N}, ::NTuple{N, Bool}) where {N} = A
932,491✔
159
_maybe_reshape_parent(A::AbstractArray, ::NTuple{N, Bool}) where {N} = reshape(A, Val(N))
361✔
160
# The trailing singleton indices could be eliminated after bounds checking.
161
rm_singleton_indices(ndims::Tuple, J1, Js...) = (J1, rm_singleton_indices(IteratorsMD._splitrest(ndims, index_ndims(J1)), Js...)...)
1,843,494✔
162
rm_singleton_indices(::Tuple{}, ::ScalarIndex, Js...) = rm_singleton_indices((), Js...)
12✔
163
rm_singleton_indices(::Tuple) = ()
33,089✔
164

165
"""
166
    view(A, inds...)
167

168
Like [`getindex`](@ref), but returns a lightweight array that lazily references
169
(or is effectively a _view_ into) the parent array `A` at the given index or indices
170
`inds` instead of eagerly extracting elements or constructing a copied subset.
171
Calling [`getindex`](@ref) or [`setindex!`](@ref) on the returned value
172
(often a [`SubArray`](@ref)) computes the indices to access or modify the
173
parent array on the fly.  The behavior is undefined if the shape of the parent array is
174
changed after `view` is called because there is no bound check for the parent array; e.g.,
175
it may cause a segmentation fault.
176

177
Some immutable parent arrays (like ranges) may choose to simply
178
recompute a new array in some circumstances instead of returning
179
a `SubArray` if doing so is efficient and provides compatible semantics.
180

181
!!! compat "Julia 1.6"
182
    In Julia 1.6 or later, `view` can be called on an `AbstractString`, returning a
183
    `SubString`.
184

185
# Examples
186
```jldoctest
187
julia> A = [1 2; 3 4]
188
2×2 Matrix{Int64}:
189
 1  2
190
 3  4
191

192
julia> b = view(A, :, 1)
193
2-element view(::Matrix{Int64}, :, 1) with eltype Int64:
194
 1
195
 3
196

197
julia> fill!(b, 0)
198
2-element view(::Matrix{Int64}, :, 1) with eltype Int64:
199
 0
200
 0
201

202
julia> A # Note A has changed even though we modified b
203
2×2 Matrix{Int64}:
204
 0  2
205
 0  4
206

207
julia> view(2:5, 2:3) # returns a range as type is immutable
208
3:4
209
```
210
"""
211
function view(A::AbstractArray, I::Vararg{Any,M}) where {M}
5,188✔
212
    @inline
30,607,015✔
213
    J = map(i->unalias(A,i), to_indices(A, I))
61,499,186✔
214
    @boundscheck checkbounds(A, J...)
51,589,944✔
215
    J′ = rm_singleton_indices(ntuple(Returns(true), Val(ndims(A))), J...)
30,606,996✔
216
    unsafe_view(_maybe_reshape_parent(A, index_ndims(J′...)), J′...)
51,589,213✔
217
end
218

219
# Ranges implement getindex to return recomputed ranges; use that for views, too (when possible)
220
function view(r1::AbstractUnitRange, r2::AbstractUnitRange{<:Integer})
6✔
221
    @_propagate_inbounds_meta
134,469✔
222
    getindex(r1, r2)
152,165✔
223
end
224
function view(r1::AbstractUnitRange, r2::StepRange{<:Integer})
1✔
225
    @_propagate_inbounds_meta
1✔
226
    getindex(r1, r2)
1✔
227
end
228
function view(r1::StepRange, r2::AbstractRange{<:Integer})
1✔
229
    @_propagate_inbounds_meta
21✔
230
    getindex(r1, r2)
41✔
231
end
232
function view(r1::StepRangeLen, r2::OrdinalRange{<:Integer})
233
    @_propagate_inbounds_meta
×
234
    getindex(r1, r2)
×
235
end
236
function view(r1::LinRange, r2::OrdinalRange{<:Integer})
×
237
    @_propagate_inbounds_meta
×
238
    getindex(r1, r2)
×
239
end
240

241
# getindex(r::AbstractRange, ::Colon) returns a copy of the range, and we may do the same for a view
242
function view(r1::AbstractRange, c::Colon)
2✔
243
    @_propagate_inbounds_meta
2✔
244
    getindex(r1, c)
2✔
245
end
246

247
function unsafe_view(A::AbstractArray, I::Vararg{ViewIndex,N}) where {N}
248
    @inline
29,960,745✔
249
    SubArray(A, I)
51,438,561✔
250
end
251
# When we take the view of a view, it's often possible to "reindex" the parent
252
# view's indices such that we can "pop" the parent view and keep just one layer
253
# of indirection. But we can't always do this because arrays of `CartesianIndex`
254
# might span multiple parent indices, making the reindex calculation very hard.
255
# So we use _maybe_reindex to figure out if there are any arrays of
256
# `CartesianIndex`, and if so, we punt and keep two layers of indirection.
257
unsafe_view(V::SubArray, I::Vararg{ViewIndex,N}) where {N} =
258
    (@inline; _maybe_reindex(V, I))
150,574✔
259
_maybe_reindex(V, I) = (@inline; _maybe_reindex(V, I, I))
150,574✔
260
_maybe_reindex(V, I, ::Tuple{AbstractArray{<:AbstractCartesianIndex}, Vararg{Any}}) =
261
    (@inline; SubArray(V, I))
17✔
262
# But allow arrays of CartesianIndex{1}; they behave just like arrays of Ints
263
_maybe_reindex(V, I, A::Tuple{AbstractArray{<:AbstractCartesianIndex{1}}, Vararg{Any}}) =
×
264
    (@inline; _maybe_reindex(V, I, tail(A)))
×
265
_maybe_reindex(V, I, A::Tuple{Any, Vararg{Any}}) = (@inline; _maybe_reindex(V, I, tail(A)))
157,411✔
266
function _maybe_reindex(V, I, ::Tuple{})
267
    @inline
132,900✔
268
    @inbounds idxs = to_indices(V.parent, reindex(V.indices, I))
150,558✔
269
    SubArray(V.parent, idxs)
150,468✔
270
end
271

272
## Re-indexing is the heart of a view, transforming A[i, j][x, y] to A[i[x], j[y]]
273
#
274
# Recursively look through the heads of the parent- and sub-indices, considering
275
# the following cases:
276
# * Parent index is array  -> re-index that with one or more sub-indices (one per dimension)
277
# * Parent index is Colon  -> just use the sub-index as provided
278
# * Parent index is scalar -> that dimension was dropped, so skip the sub-index and use the index as is
279

280
AbstractZeroDimArray{T} = AbstractArray{T, 0}
281

282
reindex(::Tuple{}, ::Tuple{}) = ()
24,786,505✔
283

284
# Skip dropped scalars, so simply peel them off the parent indices and continue
285
reindex(idxs::Tuple{ScalarIndex, Vararg{Any}}, subidxs::Tuple{Vararg{Any}}) =
286
    (@_propagate_inbounds_meta; (idxs[1], reindex(tail(idxs), subidxs)...))
2,224,195✔
287

288
# Slices simply pass their subindices straight through
289
reindex(idxs::Tuple{Slice, Vararg{Any}}, subidxs::Tuple{Any, Vararg{Any}}) =
290
    (@_propagate_inbounds_meta; (subidxs[1], reindex(tail(idxs), tail(subidxs))...))
11,222,425✔
291

292
# Re-index into parent vectors with one subindex
293
reindex(idxs::Tuple{AbstractVector, Vararg{Any}}, subidxs::Tuple{Any, Vararg{Any}}) =
294
    (@_propagate_inbounds_meta; (maybeview(idxs[1], subidxs[1]), reindex(tail(idxs), tail(subidxs))...))
33,400,558✔
295

296
# Parent matrices are re-indexed with two sub-indices
297
reindex(idxs::Tuple{AbstractMatrix, Vararg{Any}}, subidxs::Tuple{Any, Any, Vararg{Any}}) =
298
    (@_propagate_inbounds_meta; (maybeview(idxs[1], subidxs[1], subidxs[2]), reindex(tail(idxs), tail(tail(subidxs)))...))
4,639✔
299

300
# In general, we index N-dimensional parent arrays with N indices
301
@generated function reindex(idxs::Tuple{AbstractArray{T,N}, Vararg{Any}}, subidxs::Tuple{Vararg{Any}}) where {T,N}
302
    if length(subidxs.parameters) >= N
4✔
303
        subs = [:(subidxs[$d]) for d in 1:N]
×
304
        tail = [:(subidxs[$d]) for d in N+1:length(subidxs.parameters)]
×
305
        :(@_propagate_inbounds_meta; (maybeview(idxs[1], $(subs...)), reindex(tail(idxs), ($(tail...),))...))
×
306
    else
307
        :(throw(ArgumentError("cannot re-index SubArray with fewer indices than dimensions\nThis should not occur; please submit a bug report.")))
4✔
308
    end
309
end
310

311
# In general, we simply re-index the parent indices by the provided ones
312
SlowSubArray{T,N,P,I} = SubArray{T,N,P,I,false}
313
function getindex(V::SubArray{T,N}, I::Vararg{Int,N}) where {T,N}
5,645✔
314
    @inline
13,964,839✔
315
    @boundscheck checkbounds(V, I...)
13,964,854✔
316
    @inbounds r = V.parent[reindex(V.indices, I)...]
14,910,657✔
317
    r
13,964,824✔
318
end
319

320
# But SubArrays with fast linear indexing pre-compute a stride and offset
321
FastSubArray{T,N,P,I} = SubArray{T,N,P,I,true}
322
# We define a convenience functions to compute the shifted parent index
323
# This differs from reindex as this accepts the view directly, instead of its indices
324
@inline _reindexlinear(V::FastSubArray, i::Int) = V.offset1 + V.stride1*i
21,748,405✔
325
@inline _reindexlinear(V::FastSubArray, i::AbstractUnitRange{Int}) = V.offset1 .+ V.stride1 .* i
50✔
326

327
function getindex(V::FastSubArray, i::Int)
7✔
328
    @inline
8,382✔
329
    @boundscheck checkbounds(V, i)
8,384✔
330
    @inbounds r = V.parent[_reindexlinear(V, i)]
8,380✔
331
    r
8,380✔
332
end
333

334
# For vector views with linear indexing, we disambiguate to favor the stride/offset
335
# computation as that'll generally be faster than (or just as fast as) re-indexing into a range.
336
function getindex(V::FastSubArray{<:Any, 1}, i::Int)
23✔
337
    @inline
286,582,994✔
338
    @boundscheck checkbounds(V, i)
381,570,787✔
339
    @inbounds r = V.parent[_reindexlinear(V, i)]
381,570,775✔
340
    r
286,582,988✔
341
end
342

343
# We can avoid a multiplication if the first parent index is a Colon or AbstractUnitRange,
344
# or if all the indices are scalars, i.e. the view is for a single value only
345
FastContiguousSubArray{T,N,P,I<:Union{Tuple{Union{Slice, AbstractUnitRange}, Vararg{Any}},
346
                                      Tuple{Vararg{ScalarIndex}}}} = SubArray{T,N,P,I,true}
347

348
@inline _reindexlinear(V::FastContiguousSubArray, i::Int) = V.offset1 + i
711,517,883✔
349
@inline _reindexlinear(V::FastContiguousSubArray, i::AbstractUnitRange{Int}) = V.offset1 .+ i
527✔
350

351
# parents of FastContiguousSubArrays may support fast indexing with AbstractUnitRanges,
352
# so we may just forward the indexing to the parent
353
# This may only be done for non-offset ranges, as the result would otherwise have offset axes
354
const OneBasedRanges = Union{OneTo{Int}, UnitRange{Int}, Slice{OneTo{Int}}, IdentityUnitRange{OneTo{Int}}}
355
function getindex(V::FastContiguousSubArray, i::OneBasedRanges)
525✔
356
    @inline
527✔
357
    @boundscheck checkbounds(V, i)
527✔
358
    @inbounds r = V.parent[_reindexlinear(V, i)]
1,054✔
359
    r
527✔
360
end
361

362
@inline getindex(V::FastContiguousSubArray, i::Colon) = getindex(V, to_indices(V, (:,))...)
×
363

364
# Indexed assignment follows the same pattern as `getindex` above
365
function setindex!(V::SubArray{T,N}, x, I::Vararg{Int,N}) where {T,N}
188,117✔
366
    @inline
9,608,427✔
367
    @boundscheck checkbounds(V, I...)
9,608,491✔
368
    @inbounds V.parent[reindex(V.indices, I)...] = x
9,608,465✔
369
    V
9,608,414✔
370
end
371
function setindex!(V::FastSubArray, x, i::Int)
372
    @inline
1,022,123✔
373
    @boundscheck checkbounds(V, i)
1,022,123✔
374
    @inbounds V.parent[_reindexlinear(V, i)] = x
1,022,123✔
375
    V
1,022,123✔
376
end
377
function setindex!(V::FastSubArray{<:Any, 1}, x, i::Int)
378
    @inline
295,532,171✔
379
    @boundscheck checkbounds(V, i)
320,445,464✔
380
    @inbounds V.parent[_reindexlinear(V, i)] = x
320,445,464✔
381
    V
295,532,171✔
382
end
383

384
function setindex!(V::FastSubArray, x, i::AbstractUnitRange{Int})
385
    @inline
50✔
386
    @boundscheck checkbounds(V, i)
50✔
387
    @inbounds V.parent[_reindexlinear(V, i)] = x
95✔
388
    V
50✔
389
end
390

391
@inline setindex!(V::FastSubArray, x, i::Colon) = setindex!(V, x, to_indices(V, (i,))...)
50✔
392

393
function isassigned(V::SubArray{T,N}, I::Vararg{Int,N}) where {T,N}
5,637✔
394
    @inline
1,080,366✔
395
    @boundscheck checkbounds(Bool, V, I...) || return false
1,080,366✔
396
    @inbounds r = isassigned(V.parent, reindex(V.indices, I)...)
1,081,436✔
397
    r
1,080,366✔
398
end
399
function isassigned(V::FastSubArray, i::Int)
400
    @inline
857✔
401
    @boundscheck checkbounds(Bool, V, i) || return false
857✔
402
    @inbounds r = isassigned(V.parent, _reindexlinear(V, i))
857✔
403
    r
857✔
404
end
405
function isassigned(V::FastSubArray{<:Any, 1}, i::Int)
406
    @inline
12,084,212✔
407
    @boundscheck checkbounds(Bool, V, i) || return false
30,218,692✔
408
    @inbounds r = isassigned(V.parent, _reindexlinear(V, i))
30,218,692✔
409
    r
12,084,212✔
410
end
411

412
function _unsetindex!(V::FastSubArray, i::Int)
413
    @inline
×
414
    @boundscheck checkbounds(V, i)
×
415
    @inbounds _unsetindex!(V.parent, _reindexlinear(V, i))
×
416
    return V
×
417
end
418
function _unsetindex!(V::FastSubArray{<:Any,1}, i::Int)
419
    @inline
12✔
420
    @boundscheck checkbounds(V, i)
12✔
421
    @inbounds _unsetindex!(V.parent, _reindexlinear(V, i))
12✔
422
    return V
12✔
423
end
424
function _unsetindex!(V::SubArray{T,N}, i::Vararg{Int,N}) where {T,N}
425
    @inline
×
426
    @boundscheck checkbounds(V, i...)
×
427
    @inbounds _unsetindex!(V.parent, reindex(V.indices, i)...)
×
428
    return V
×
429
end
430

431
IndexStyle(::Type{<:FastSubArray}) = IndexLinear()
2,854,006✔
432

433
# Strides are the distance in memory between adjacent elements in a given dimension
434
# which we determine from the strides of the parent
435
strides(V::SubArray) = substrides(strides(V.parent), V.indices)
68,539✔
436

437
substrides(strds::Tuple{}, ::Tuple{}) = ()
68,539✔
438
substrides(strds::NTuple{N,Int}, I::Tuple{ScalarIndex, Vararg{Any}}) where N = (substrides(tail(strds), tail(I))...,)
2,808✔
439
substrides(strds::NTuple{N,Int}, I::Tuple{Slice, Vararg{Any}}) where N = (first(strds), substrides(tail(strds), tail(I))...)
6,313✔
440
substrides(strds::NTuple{N,Int}, I::Tuple{AbstractRange, Vararg{Any}}) where N = (first(strds)*step(I[1]), substrides(tail(strds), tail(I))...)
149,281✔
441
substrides(strds, I::Tuple{Any, Vararg{Any}}) = throw(ArgumentError("strides is invalid for SubArrays with indices of type $(typeof(I[1]))"))
×
442

443
stride(V::SubArray, d::Integer) = d <= ndims(V) ? strides(V)[d] : strides(V)[end] * size(V)[end]
32,425✔
444

445
compute_stride1(parent::AbstractArray, I::NTuple{N,Any}) where {N} =
446
    (@inline; compute_stride1(1, fill_to_length(axes(parent), OneTo(1), Val(N)), I))
29,555,384✔
447
compute_stride1(s, inds, I::Tuple{}) = s
1✔
448
compute_stride1(s, inds, I::Tuple{Vararg{ScalarIndex}}) = s
50✔
449
compute_stride1(s, inds, I::Tuple{ScalarIndex, Vararg{Any}}) =
450
    (@inline; compute_stride1(s*length(inds[1]), tail(inds), tail(I)))
261,689✔
451
compute_stride1(s, inds, I::Tuple{AbstractRange, Vararg{Any}}) = s*step(I[1])
455,548✔
452
compute_stride1(s, inds, I::Tuple{Slice, Vararg{Any}}) = s
290,742✔
453
compute_stride1(s, inds, I::Tuple{Any, Vararg{Any}}) = throw(ArgumentError("invalid strided index type $(typeof(I[1]))"))
×
454

455
elsize(::Type{<:SubArray{<:Any,<:Any,P}}) where {P} = elsize(P)
17,563,336✔
456

457
iscontiguous(A::SubArray) = iscontiguous(typeof(A))
×
458
iscontiguous(::Type{<:SubArray}) = false
×
459
iscontiguous(::Type{<:FastContiguousSubArray}) = true
×
460

461
first_index(V::FastSubArray) = V.offset1 + V.stride1 * firstindex(V) # cached for fast linear SubArrays
17,597,096✔
462
first_index(V::SubArray) = compute_linindex(parent(V), V.indices)
16,896✔
463

464
# Computing the first index simply steps through the indices, accumulating the
465
# sum of index each multiplied by the parent's stride.
466
# The running sum is `f`; the cumulative stride product is `s`.
467
# If the parent is a vector, then we offset the parent's own indices with parameters of I
468
compute_offset1(parent::AbstractVector, stride1::Integer, I::Tuple{AbstractRange}) =
469
    (@inline; first(I[1]) - stride1*first(axes1(I[1])))
49,042,067✔
470
# If the result is one-dimensional and it's a Colon, then linear
471
# indexing uses the indices along the given dimension.
472
# If the result is one-dimensional and it's a range, then linear
473
# indexing might be offset if the index itself is offset
474
# Otherwise linear indexing always matches the parent.
475
compute_offset1(parent, stride1::Integer, I::Tuple) =
476
    (@inline; compute_offset1(parent, stride1, find_extended_dims(1, I...), find_extended_inds(I...), I))
711,038✔
477
compute_offset1(parent, stride1::Integer, dims::Tuple{Int}, inds::Tuple{Slice}, I::Tuple) =
478
    (@inline; compute_linindex(parent, I) - stride1*first(axes(parent, dims[1])))  # index-preserving case
259,141✔
479
compute_offset1(parent, stride1::Integer, dims, inds::Tuple{AbstractRange}, I::Tuple) =
480
    (@inline; compute_linindex(parent, I) - stride1*first(axes1(inds[1]))) # potentially index-offsetting case
422,107✔
481
compute_offset1(parent, stride1::Integer, dims, inds, I::Tuple) =
482
    (@inline; compute_linindex(parent, I) - stride1)
29,790✔
483
function compute_linindex(parent, I::NTuple{N,Any}) where N
484
    @inline
727,934✔
485
    IP = fill_to_length(axes(parent), OneTo(1), Val(N))
727,934✔
486
    compute_linindex(first(LinearIndices(parent)), 1, IP, I)
727,934✔
487
end
488
function compute_linindex(f, s, IP::Tuple, I::Tuple{Any, Vararg{Any}})
489
    @inline
1,464,882✔
490
    Δi = first(I[1])-first(IP[1])
1,464,882✔
491
    compute_linindex(f + Δi*s, s*length(IP[1]), tail(IP), tail(I))
1,464,882✔
492
end
493
compute_linindex(f, s, IP::Tuple, I::Tuple{}) = f
727,934✔
494

495
find_extended_dims(dim, ::ScalarIndex, I...) = (@inline; find_extended_dims(dim + 1, I...))
686,347✔
496
find_extended_dims(dim, i1, I...) = (@inline; (dim, find_extended_dims(dim + 1, I...)...))
741,811✔
497
find_extended_dims(dim) = ()
711,038✔
498
find_extended_inds(::ScalarIndex, I...) = (@inline; find_extended_inds(I...))
686,347✔
499
find_extended_inds(i1, I...) = (@inline; (i1, find_extended_inds(I...)...))
741,811✔
500
find_extended_inds() = ()
711,038✔
501

502
pointer(V::FastSubArray, i::Int) = pointer(V.parent, V.offset1 + V.stride1*i)
×
503
pointer(V::FastContiguousSubArray, i::Int) = pointer(V.parent, V.offset1 + i)
9✔
504

505
function pointer(V::SubArray{<:Any,<:Any,<:Array,<:Tuple{Vararg{RangeIndex}}}, is::AbstractCartesianIndex{N}) where {N}
×
506
    index = first_index(V)
×
507
    strds = strides(V)
×
508
    for d = 1:N
×
509
        index += (is[d]-1)*strds[d]
×
510
    end
×
511
    return pointer(V.parent, index)
×
512
end
513

514
# indices are taken from the range/vector
515
# Since bounds-checking is performance-critical and uses
516
# indices, it's worth optimizing these implementations thoroughly
517
axes(S::SubArray) = (@inline; _indices_sub(S.indices...))
820,368,410✔
518
_indices_sub(::Real, I...) = (@inline; _indices_sub(I...))
15,766,623✔
519
_indices_sub() = ()
×
520
function _indices_sub(i1::AbstractArray, I...)
103✔
521
    @inline
50,609,182✔
522
    (axes(i1)..., _indices_sub(I...)...)
814,672,841✔
523
end
524

525
has_offset_axes(S::SubArray) = has_offset_axes(S.indices...)
638,302✔
526

527
function replace_in_print_matrix(S::SubArray{<:Any,2,<:AbstractMatrix}, i::Integer, j::Integer, s::AbstractString)
1✔
528
    replace_in_print_matrix(S.parent, to_indices(S.parent, reindex(S.indices, (i,j)))..., s)
1✔
529
end
530
function replace_in_print_matrix(S::SubArray{<:Any,1,<:AbstractVector}, i::Integer, j::Integer, s::AbstractString)
×
531
    replace_in_print_matrix(S.parent, to_indices(S.parent, reindex(S.indices, (i,)))..., j, s)
×
532
end
533

534
# XXX: this is considerably more unsafe than the other similarly named methods
535
unsafe_wrap(::Type{Vector{UInt8}}, s::FastContiguousSubArray{UInt8,1,Vector{UInt8}}) = unsafe_wrap(Vector{UInt8}, pointer(s), size(s))
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc