• 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

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

3
using  Base.MultiplicativeInverses: SignedMultiplicativeInverse
4

5
struct ReshapedArray{T,N,P<:AbstractArray,MI<:Tuple{Vararg{SignedMultiplicativeInverse{Int}}}} <: AbstractArray{T,N}
6
    parent::P
124,458✔
7
    dims::NTuple{N,Int}
8
    mi::MI
9
end
10
ReshapedArray(parent::AbstractArray{T}, dims::NTuple{N,Int}, mi) where {T,N} = ReshapedArray{T,N,typeof(parent),typeof(mi)}(parent, dims, mi)
124,452✔
11

12
# IndexLinear ReshapedArray
13
const ReshapedArrayLF{T,N,P<:AbstractArray} = ReshapedArray{T,N,P,Tuple{}}
14

15
# Fast iteration on ReshapedArrays: use the parent iterator
16
struct ReshapedArrayIterator{I,M}
17
    iter::I
18
    mi::NTuple{M,SignedMultiplicativeInverse{Int}}
19
end
20
ReshapedArrayIterator(A::ReshapedArray) = _rs_iterator(parent(A), A.mi)
×
21
function _rs_iterator(P, mi::NTuple{M}) where M
×
22
    iter = eachindex(P)
×
23
    ReshapedArrayIterator{typeof(iter),M}(iter, mi)
×
24
end
25

26
struct ReshapedIndex{T}
27
    parentindex::T
4✔
28
end
29

30
# eachindex(A::ReshapedArray) = ReshapedArrayIterator(A)  # TODO: uncomment this line
31
@inline function iterate(R::ReshapedArrayIterator, i...)
×
32
    item, inext = iterate(R.iter, i...)
×
33
    ReshapedIndex(item), inext
×
34
end
35
length(R::ReshapedArrayIterator) = length(R.iter)
×
36
eltype(::Type{<:ReshapedArrayIterator{I}}) where {I} = @isdefined(I) ? ReshapedIndex{eltype(I)} : Any
×
37

38
## reshape(::Array, ::Dims) returns an Array, except for isbitsunion eltypes (issue #28611)
39
# reshaping to same # of dimensions
40
@eval function reshape(a::Array{T,M}, dims::NTuple{N,Int}) where {T,N,M}
117✔
41
    throw_dmrsa(dims, len) =
43,007✔
42
        throw(DimensionMismatch("new dimensions $(dims) must be consistent with array length $len"))
43
    len = Core.checked_dims(dims...) # make sure prod(dims) doesn't overflow (and because of the comparison to length(a))
43,003✔
44
    if len != length(a)
43,003✔
45
        throw_dmrsa(dims, length(a))
4✔
46
    end
47
    isbitsunion(T) && return ReshapedArray(a, dims, ())
42,999✔
48
    if N == M && dims == size(a)
43,254✔
49
        return a
305✔
50
    end
51
    ref = a.ref
42,642✔
52
    if M == 1 && N !== 1
42,642✔
53
        mem = ref.mem::Memory{T}
7,921✔
54
        if !(ref === GenericMemoryRef(mem) && len === mem.length)
7,921✔
55
            mem = ccall(:jl_genericmemory_slice, Memory{T}, (Any, Ptr{Cvoid}, Int), mem, ref.ptr_or_offset, len)
9✔
56
            ref = GenericMemoryRef(mem)::typeof(ref)
9✔
57
        end
58
    end
59
    # or we could use `a = Array{T,N}(undef, ntuple(0, Val(N))); a.ref = ref; a.size = dims; return a` here
60
    return $(Expr(:new, :(Array{T,N}), :ref, :dims))
42,642✔
61
end
62

63

64
"""
65
    reshape(A, dims...) -> AbstractArray
66
    reshape(A, dims) -> AbstractArray
67

68
Return an array with the same data as `A`, but with different
69
dimension sizes or number of dimensions. The two arrays share the same
70
underlying data, so that the result is mutable if and only if `A` is
71
mutable, and setting elements of one alters the values of the other.
72

73
The new dimensions may be specified either as a list of arguments or
74
as a shape tuple. At most one dimension may be specified with a `:`,
75
in which case its length is computed such that its product with all
76
the specified dimensions is equal to the length of the original array
77
`A`. The total number of elements must not change.
78

79
# Examples
80
```jldoctest
81
julia> A = Vector(1:16)
82
16-element Vector{Int64}:
83
  1
84
  2
85
  3
86
  4
87
  5
88
  6
89
  7
90
  8
91
  9
92
 10
93
 11
94
 12
95
 13
96
 14
97
 15
98
 16
99

100
julia> reshape(A, (4, 4))
101
4×4 Matrix{Int64}:
102
 1  5   9  13
103
 2  6  10  14
104
 3  7  11  15
105
 4  8  12  16
106

107
julia> reshape(A, 2, :)
108
2×8 Matrix{Int64}:
109
 1  3  5  7   9  11  13  15
110
 2  4  6  8  10  12  14  16
111

112
julia> reshape(1:6, 2, 3)
113
2×3 reshape(::UnitRange{Int64}, 2, 3) with eltype Int64:
114
 1  3  5
115
 2  4  6
116
```
117
"""
118
reshape
119

120
reshape(parent::AbstractArray, dims::IntOrInd...) = reshape(parent, dims)
1✔
121
reshape(parent::AbstractArray, shp::Tuple{Union{Integer,OneTo}, Vararg{Union{Integer,OneTo}}}) = reshape(parent, to_shape(shp))
33,619✔
122
reshape(parent::AbstractArray, dims::Dims)        = _reshape(parent, dims)
6,535✔
123

124
# Allow missing dimensions with Colon():
125
reshape(parent::AbstractVector, ::Colon) = parent
3✔
126
reshape(parent::AbstractVector, ::Tuple{Colon}) = parent
2✔
127
reshape(parent::AbstractArray, dims::Int...) = reshape(parent, dims)
8,250✔
128
reshape(parent::AbstractArray, dims::Union{Int,Colon}...) = reshape(parent, dims)
90✔
129
reshape(parent::AbstractArray, dims::Tuple{Vararg{Union{Int,Colon}}}) = reshape(parent, _reshape_uncolon(parent, dims))
96✔
130
@inline function _reshape_uncolon(A, dims)
131
    @noinline throw1(dims) = throw(DimensionMismatch(string("new dimensions $(dims) ",
97✔
132
        "may have at most one omitted dimension specified by `Colon()`")))
133
    @noinline throw2(A, dims) = throw(DimensionMismatch(string("array size $(length(A)) ",
98✔
134
        "must be divisible by the product of the new dimensions $dims")))
135
    pre = _before_colon(dims...)
95✔
136
    post = _after_colon(dims...)
95✔
137
    _any_colon(post...) && throw1(dims)
95✔
138
    sz, remainder = divrem(length(A), prod(pre)*prod(post))
93✔
139
    remainder == 0 || throw2(A, dims)
96✔
140
    (pre..., Int(sz), post...)
90✔
141
end
142
@inline _any_colon() = false
×
143
@inline _any_colon(dim::Colon, tail...) = true
×
144
@inline _any_colon(dim::Any, tail...) = _any_colon(tail...)
45✔
145
@inline _before_colon(dim::Any, tail...) = (dim, _before_colon(tail...)...)
31✔
146
@inline _before_colon(dim::Colon, tail...) = ()
95✔
147
@inline _after_colon(dim::Any, tail...) =  _after_colon(tail...)
31✔
148
@inline _after_colon(dim::Colon, tail...) = tail
95✔
149

150
reshape(parent::AbstractArray{T,N}, ndims::Val{N}) where {T,N} = parent
319,251✔
151
function reshape(parent::AbstractArray, ndims::Val{N}) where N
31✔
152
    reshape(parent, rdims(Val(N), axes(parent)))
33,597✔
153
end
154

155
# Move elements from inds to out until out reaches the desired
156
# dimensionality N, either filling with OneTo(1) or collapsing the
157
# product of trailing dims into the last element
158
rdims_trailing(l, inds...) = length(l) * rdims_trailing(inds...)
33,351✔
159
rdims_trailing(l) = length(l)
33,170✔
160
rdims(out::Val{N}, inds::Tuple) where {N} = rdims(ntuple(Returns(OneTo(1)), Val(N)), inds)
33,597✔
161
rdims(out::Tuple{}, inds::Tuple{}) = () # N == 0, M == 0
×
162
rdims(out::Tuple{}, inds::Tuple{Any}) = ()
6✔
163
rdims(out::Tuple{}, inds::NTuple{M,Any}) where {M} = ()
×
164
rdims(out::Tuple{Any}, inds::Tuple{}) = out # N == 1, M == 0
413✔
165
rdims(out::NTuple{N,Any}, inds::Tuple{}) where {N} = out # N > 1, M == 0
8✔
166
rdims(out::Tuple{Any}, inds::Tuple{Any}) = inds # N == 1, M == 1
×
167
rdims(out::Tuple{Any}, inds::NTuple{M,Any}) where {M} = (oneto(rdims_trailing(inds...)),) # N == 1, M > 1
33,170✔
168
rdims(out::NTuple{N,Any}, inds::NTuple{N,Any}) where {N} = inds # N > 1, M == N
×
169
rdims(out::NTuple{N,Any}, inds::NTuple{M,Any}) where {N,M} = (first(inds), rdims(tail(out), tail(inds))...) # N > 1, M > 1, M != N
465✔
170

171

172
# _reshape on Array returns an Array
173
_reshape(parent::Vector, dims::Dims{1}) = parent
2✔
174
_reshape(parent::Array, dims::Dims{1}) = reshape(parent, dims)
×
175
_reshape(parent::Array, dims::Dims) = reshape(parent, dims)
×
176

177
# When reshaping Vector->Vector, don't wrap with a ReshapedArray
178
function _reshape(v::AbstractVector, dims::Dims{1})
179
    require_one_based_indexing(v)
27✔
180
    len = dims[1]
27✔
181
    len == length(v) || _throw_dmrs(length(v), "length", len)
28✔
182
    v
26✔
183
end
184
# General reshape
185
function _reshape(parent::AbstractArray, dims::Dims)
30✔
186
    n = length(parent)
6,501✔
187
    prod(dims) == n || _throw_dmrs(n, "size", dims)
6,514✔
188
    __reshape((parent, IndexStyle(parent)), dims)
6,488✔
189
end
190

191
@noinline function _throw_dmrs(n, str, dims)
14✔
192
    throw(DimensionMismatch("parent has $n elements, which is incompatible with $str $dims"))
14✔
193
end
194

195
# Reshaping a ReshapedArray
196
_reshape(v::ReshapedArray{<:Any,1}, dims::Dims{1}) = _reshape(v.parent, dims)
2✔
197
_reshape(R::ReshapedArray, dims::Dims) = _reshape(R.parent, dims)
20✔
198

199
function __reshape(p::Tuple{AbstractArray,IndexStyle}, dims::Dims)
30✔
200
    parent = p[1]
1,922✔
201
    strds = front(size_to_strides(map(length, axes(parent))..., 1))
1,922✔
202
    strds1 = map(s->max(1,Int(s)), strds)  # for resizing empty arrays
3,426✔
203
    mi = map(SignedMultiplicativeInverse, strds1)
1,922✔
204
    ReshapedArray(parent, dims, reverse(mi))
1,922✔
205
end
206

207
function __reshape(p::Tuple{AbstractArray{<:Any,0},IndexCartesian}, dims::Dims)
208
    parent = p[1]
4✔
209
    ReshapedArray(parent, dims, ())
4✔
210
end
211

212
function __reshape(p::Tuple{AbstractArray,IndexLinear}, dims::Dims)
213
    parent = p[1]
4,562✔
214
    ReshapedArray(parent, dims, ())
4,562✔
215
end
216

217
size(A::ReshapedArray) = A.dims
3,965,187✔
218
length(A::ReshapedArray) = length(parent(A))
16,068,544✔
219
similar(A::ReshapedArray, eltype::Type, dims::Dims) = similar(parent(A), eltype, dims)
558✔
220
IndexStyle(::Type{<:ReshapedArrayLF}) = IndexLinear()
20,425✔
221
parent(A::ReshapedArray) = A.parent
33,204,387✔
222
parentindices(A::ReshapedArray) = map(oneto, size(parent(A)))
2✔
223
reinterpret(::Type{T}, A::ReshapedArray, dims::Dims) where {T} = reinterpret(T, parent(A), dims)
×
224
elsize(::Type{<:ReshapedArray{<:Any,<:Any,P}}) where {P} = elsize(P)
1,646✔
225

226
unaliascopy(A::ReshapedArray) = typeof(A)(unaliascopy(A.parent), A.dims, A.mi)
6✔
227
dataids(A::ReshapedArray) = dataids(A.parent)
4,510✔
228
# forward the aliasing check the parent in case there are specializations
229
mightalias(A::ReshapedArray, B::ReshapedArray) = mightalias(parent(A), parent(B))
1✔
230
# special handling for reshaped SubArrays that dispatches to the subarray aliasing check
231
mightalias(A::ReshapedArray, B::SubArray) = mightalias(parent(A), B)
×
232
mightalias(A::SubArray, B::ReshapedArray) = mightalias(A, parent(B))
3,073✔
233

234
@inline ind2sub_rs(ax, ::Tuple{}, i::Int) = (i,)
45,881✔
235
@inline ind2sub_rs(ax, strds, i) = _ind2sub_rs(ax, strds, i - 1)
1,757,349✔
236
@inline _ind2sub_rs(ax, ::Tuple{}, ind) = (ind + first(ax[end]),)
1,757,349✔
237
@inline function _ind2sub_rs(ax, strds, ind)
238
    d, r = divrem(ind, strds[1])
3,335,377✔
239
    (_ind2sub_rs(front(ax), tail(strds), r)..., d + first(ax[end]))
3,335,377✔
240
end
241
offset_if_vec(i::Integer, axs::Tuple{<:AbstractUnitRange}) = i + first(axs[1]) - 1
37,138✔
242
offset_if_vec(i::Integer, axs::Tuple) = i
1,636,456✔
243

244
@inline function isassigned(A::ReshapedArrayLF, index::Int)
245
    @boundscheck checkbounds(Bool, A, index) || return false
4,002,741✔
246
    indexparent = index - firstindex(A) + firstindex(parent(A))
4,002,741✔
247
    @inbounds ret = isassigned(parent(A), indexparent)
4,002,741✔
248
    ret
4,002,741✔
249
end
250
@inline function isassigned(A::ReshapedArray{T,N}, indices::Vararg{Int, N}) where {T,N}
14,100✔
251
    @boundscheck checkbounds(Bool, A, indices...) || return false
591,914✔
252
    axp = axes(A.parent)
591,914✔
253
    i = offset_if_vec(_sub2ind(size(A), indices...), axp)
591,914✔
254
    I = ind2sub_rs(axp, A.mi, i)
591,914✔
255
    @inbounds isassigned(A.parent, I...)
591,914✔
256
end
257

258
@inline function getindex(A::ReshapedArrayLF, index::Int)
6✔
259
    @boundscheck checkbounds(A, index)
4,020,338✔
260
    indexparent = index - firstindex(A) + firstindex(parent(A))
4,020,338✔
261
    @inbounds ret = parent(A)[indexparent]
4,020,538✔
262
    ret
4,020,338✔
263
end
264
@inline function getindex(A::ReshapedArray{T,N}, indices::Vararg{Int,N}) where {T,N}
14,115✔
265
    @boundscheck checkbounds(A, indices...)
1,079,894✔
266
    _unsafe_getindex(A, indices...)
1,081,328✔
267
end
268
@inline function getindex(A::ReshapedArray, index::ReshapedIndex)
2✔
269
    @boundscheck checkbounds(parent(A), index.parentindex)
2✔
270
    @inbounds ret = parent(A)[index.parentindex]
2✔
271
    ret
2✔
272
end
273

274
@inline function _unsafe_getindex(A::ReshapedArray{T,N}, indices::Vararg{Int,N}) where {T,N}
275
    axp = axes(A.parent)
1,079,890✔
276
    i = offset_if_vec(_sub2ind(size(A), indices...), axp)
1,079,890✔
277
    I = ind2sub_rs(axp, A.mi, i)
1,079,890✔
278
    _unsafe_getindex_rs(parent(A), I)
1,081,328✔
279
end
280
@inline _unsafe_getindex_rs(A, i::Integer) = (@inbounds ret = A[i]; ret)
×
281
@inline _unsafe_getindex_rs(A, I) = (@inbounds ret = A[I...]; ret)
1,081,328✔
282

283
@inline function setindex!(A::ReshapedArrayLF, val, index::Int)
3✔
284
    @boundscheck checkbounds(A, index)
263✔
285
    indexparent = index - firstindex(A) + firstindex(parent(A))
263✔
286
    @inbounds parent(A)[indexparent] = val
263✔
287
    val
263✔
288
end
289
@inline function setindex!(A::ReshapedArray{T,N}, val, indices::Vararg{Int,N}) where {T,N}
3✔
290
    @boundscheck checkbounds(A, indices...)
1,790✔
291
    _unsafe_setindex!(A, val, indices...)
1,790✔
292
end
293
@inline function setindex!(A::ReshapedArray, val, index::ReshapedIndex)
1✔
294
    @boundscheck checkbounds(parent(A), index.parentindex)
1✔
295
    @inbounds parent(A)[index.parentindex] = val
1✔
296
    val
1✔
297
end
298

299
@inline function _unsafe_setindex!(A::ReshapedArray{T,N}, val, indices::Vararg{Int,N}) where {T,N}
300
    axp = axes(A.parent)
1,790✔
301
    i = offset_if_vec(_sub2ind(size(A), indices...), axp)
1,790✔
302
    @inbounds parent(A)[ind2sub_rs(axes(A.parent), A.mi, i)...] = val
1,790✔
303
    val
1,790✔
304
end
305

306
# helpful error message for a common failure case
307
const ReshapedRange{T,N,A<:AbstractRange} = ReshapedArray{T,N,A,Tuple{}}
308
setindex!(A::ReshapedRange, val, index::Int) = _rs_setindex!_err()
1✔
309
setindex!(A::ReshapedRange{T,N}, val, indices::Vararg{Int,N}) where {T,N} = _rs_setindex!_err()
1✔
310
setindex!(A::ReshapedRange, val, index::ReshapedIndex) = _rs_setindex!_err()
1✔
311

312
@noinline _rs_setindex!_err() = error("indexed assignment fails for a reshaped range; consider calling collect")
3✔
313

314
cconvert(::Type{Ptr{T}}, a::ReshapedArray{T}) where {T} = cconvert(Ptr{T}, parent(a))
1,587✔
315

316
# Add a few handy specializations to further speed up views of reshaped ranges
317
const ReshapedUnitRange{T,N,A<:AbstractUnitRange} = ReshapedArray{T,N,A,Tuple{}}
318
viewindexing(I::Tuple{Slice, ReshapedUnitRange, Vararg{ScalarIndex}}) = IndexLinear()
×
319
viewindexing(I::Tuple{ReshapedRange, Vararg{ScalarIndex}}) = IndexLinear()
×
320
compute_stride1(s, inds, I::Tuple{ReshapedRange, Vararg{Any}}) = s*step(I[1].parent)
×
321
compute_offset1(parent::AbstractVector, stride1::Integer, I::Tuple{ReshapedRange}) =
×
322
    (@inline; first(I[1]) - first(axes1(I[1]))*stride1)
×
323
substrides(strds::NTuple{N,Int}, I::Tuple{ReshapedUnitRange, Vararg{Any}}) where N =
54✔
324
    (size_to_strides(strds[1], size(I[1])...)..., substrides(tail(strds), tail(I))...)
325

326
# cconvert(::Type{<:Ptr}, V::SubArray{T,N,P,<:Tuple{Vararg{Union{RangeIndex,ReshapedUnitRange}}}}) where {T,N,P} = V
327
function unsafe_convert(::Type{Ptr{S}}, V::SubArray{T,N,P,<:Tuple{Vararg{Union{RangeIndex,ReshapedUnitRange}}}}) where {S,T,N,P}
2✔
328
    parent = V.parent
18,058,420✔
329
    p = cconvert(Ptr{T}, parent) # XXX: this should occur in cconvert, the result is not GC-rooted
18,891,836✔
330
    Δmem = if _checkcontiguous(Bool, parent)
18,055,691✔
331
        (first_index(V) - firstindex(parent)) * elsize(parent)
18,085,625✔
332
    else
333
        _memory_offset(parent, map(first, V.indices)...)
25,572✔
334
    end
335
    return Ptr{S}(unsafe_convert(Ptr{T}, p) + Δmem)
18,891,836✔
336
end
337

338
_checkcontiguous(::Type{Bool}, A::AbstractArray) = false
×
339
# `strides(A::DenseArray)` calls `size_to_strides` by default.
340
# Thus it's OK to assume all `DenseArray`s are contiguously stored.
341
_checkcontiguous(::Type{Bool}, A::DenseArray) = true
×
342
_checkcontiguous(::Type{Bool}, A::ReshapedArray) = _checkcontiguous(Bool, parent(A))
1,905✔
343
_checkcontiguous(::Type{Bool}, A::FastContiguousSubArray) = _checkcontiguous(Bool, parent(A))
17,539,357✔
344

345
function strides(a::ReshapedArray)
616✔
346
    _checkcontiguous(Bool, a) && return size_to_strides(1, size(a)...)
1,696✔
347
    apsz::Dims = size(a.parent)
1,674✔
348
    apst::Dims = strides(a.parent)
1,674✔
349
    msz, mst, n = merge_adjacent_dim(apsz, apst) # Try to perform "lazy" reshape
1,674✔
350
    n == ndims(a.parent) && return size_to_strides(mst, size(a)...) # Parent is stridevector like
1,674✔
351
    return _reshaped_strides(size(a), 1, msz, mst, n, apsz, apst)
581✔
352
end
353

354
function _reshaped_strides(::Dims{0}, reshaped::Int, msz::Int, ::Int, ::Int, ::Dims, ::Dims)
355
    reshaped == msz && return ()
581✔
356
    throw(ArgumentError("Input is not strided."))
5✔
357
end
358
function _reshaped_strides(sz::Dims, reshaped::Int, msz::Int, mst::Int, n::Int, apsz::Dims, apst::Dims)
976✔
359
    st = reshaped * mst
2,924✔
360
    reshaped = reshaped * sz[1]
2,924✔
361
    if length(sz) > 1 && reshaped == msz && sz[2] != 1
2,924✔
362
        msz, mst, n = merge_adjacent_dim(apsz, apst, n + 1)
576✔
363
        reshaped = 1
576✔
364
    end
365
    sts = _reshaped_strides(tail(sz), reshaped, msz, mst, n, apsz, apst)
2,937✔
366
    return (st, sts...)
2,908✔
367
end
368

369
merge_adjacent_dim(::Dims{0}, ::Dims{0}) = 1, 1, 0
1✔
370
merge_adjacent_dim(apsz::Dims{1}, apst::Dims{1}) = apsz[1], apst[1], 1
3,308✔
371
function merge_adjacent_dim(apsz::Dims{N}, apst::Dims{N}, n::Int = 1) where {N}
372
    sz, st = apsz[n], apst[n]
2,408✔
373
    while n < N
1,846✔
374
        szₙ, stₙ = apsz[n+1], apst[n+1]
940✔
375
        if sz == 1
940✔
376
            sz, st = szₙ, stₙ
3✔
377
        elseif stₙ == st * sz || szₙ == 1
1,540✔
378
            sz *= szₙ
351✔
379
        else
380
            break
586✔
381
        end
382
        n += 1
354✔
383
    end
354✔
384
    return sz, st, n
1,492✔
385
end
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