Skip to content

Commit 8093a7c

Browse files
authored
Merge pull request #32448 from JuliaLang/jq/inlineunionfields
For structs with all isbits or isbitsunion fields, allow them to be s…
2 parents ef75269 + d94e2b5 commit 8093a7c

File tree

6 files changed

+47
-24
lines changed

6 files changed

+47
-24
lines changed

NEWS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ Julia v1.4 Release Notes
44
New language features
55
---------------------
66

7+
* Structs with all isbits and isbitsunion fields are now stored inline in arrays ([#32448]).
78

89
Language changes
910
----------------

base/array.jl

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,8 @@ size(a::Array{<:Any,N}) where {N} = (@_inline_meta; ntuple(M -> size(a, M), Val(
158158

159159
asize_from(a::Array, n) = n > ndims(a) ? () : (arraysize(a,n), asize_from(a, n+1)...)
160160

161+
allocatedinline(::Type{T}) where {T} = (@_pure_meta; ccall(:jl_array_store_unboxed, Cint, (Any,), T) != Cint(0))
162+
161163
"""
162164
Base.isbitsunion(::Type{T})
163165
@@ -172,14 +174,12 @@ julia> Base.isbitsunion(Union{Float64, String})
172174
false
173175
```
174176
"""
175-
isbitsunion(u::Union) = (@_pure_meta; ccall(:jl_array_store_unboxed, Cint, (Any,), u) != Cint(0))
177+
isbitsunion(u::Union) = allocatedinline(u)
176178
isbitsunion(x) = false
177179

178-
isptrelement(t::Type) = (@_pure_meta; ccall(:jl_array_store_unboxed, Cint, (Any,), t) == Cint(0))
179-
180180
function _unsetindex!(A::Array{T}, i::Int) where {T}
181181
@boundscheck checkbounds(A, i)
182-
if isptrelement(T)
182+
if !allocatedinline(T)
183183
t = @_gc_preserve_begin A
184184
p = Ptr{Ptr{Cvoid}}(pointer(A))
185185
unsafe_store!(p, C_NULL, i)
@@ -212,7 +212,7 @@ function bitsunionsize(u::Union)
212212
end
213213

214214
length(a::Array) = arraylen(a)
215-
elsize(::Type{<:Array{T}}) where {T} = isbitstype(T) ? sizeof(T) : (isbitsunion(T) ? bitsunionsize(T) : sizeof(Ptr))
215+
elsize(::Type{<:Array{T}}) where {T} = isbitsunion(T) ? bitsunionsize(T) : (allocatedinline(T) ? sizeof(T) : sizeof(Ptr))
216216
sizeof(a::Array) = Core.sizeof(a)
217217

218218
function isassigned(a::Array, i::Int...)
@@ -255,16 +255,16 @@ the same manner as C.
255255
function unsafe_copyto!(dest::Array{T}, doffs, src::Array{T}, soffs, n) where T
256256
t1 = @_gc_preserve_begin dest
257257
t2 = @_gc_preserve_begin src
258-
if isbitstype(T)
259-
unsafe_copyto!(pointer(dest, doffs), pointer(src, soffs), n)
260-
elseif isbitsunion(T)
258+
if isbitsunion(T)
261259
ccall(:memmove, Ptr{Cvoid}, (Ptr{Cvoid}, Ptr{Cvoid}, UInt),
262260
pointer(dest, doffs), pointer(src, soffs), n * Base.bitsunionsize(T))
263261
# copy selector bytes
264262
ccall(:memmove, Ptr{Cvoid}, (Ptr{Cvoid}, Ptr{Cvoid}, UInt),
265263
ccall(:jl_array_typetagdata, Ptr{UInt8}, (Any,), dest) + doffs - 1,
266264
ccall(:jl_array_typetagdata, Ptr{UInt8}, (Any,), src) + soffs - 1,
267265
n)
266+
elseif allocatedinline(T)
267+
unsafe_copyto!(pointer(dest, doffs), pointer(src, soffs), n)
268268
else
269269
ccall(:jl_array_ptr_copy, Cvoid, (Any, Ptr{Cvoid}, Any, Ptr{Cvoid}, Int),
270270
dest, pointer(dest, doffs), src, pointer(src, soffs), n)
@@ -1548,28 +1548,28 @@ function vcat(arrays::Vector{T}...) where T
15481548
end
15491549
arr = Vector{T}(undef, n)
15501550
ptr = pointer(arr)
1551-
if isbitstype(T)
1552-
elsz = Core.sizeof(T)
1553-
elseif isbitsunion(T)
1551+
if isbitsunion(T)
15541552
elsz = bitsunionsize(T)
15551553
selptr = ccall(:jl_array_typetagdata, Ptr{UInt8}, (Any,), arr)
1554+
elseif allocatedinline(T)
1555+
elsz = Core.sizeof(T)
15561556
else
15571557
elsz = Core.sizeof(Ptr{Cvoid})
15581558
end
15591559
t = @_gc_preserve_begin arr
15601560
for a in arrays
15611561
na = length(a)
15621562
nba = na * elsz
1563-
if isbitstype(T)
1564-
ccall(:memcpy, Ptr{Cvoid}, (Ptr{Cvoid}, Ptr{Cvoid}, UInt),
1565-
ptr, a, nba)
1566-
elseif isbitsunion(T)
1563+
if isbitsunion(T)
15671564
ccall(:memcpy, Ptr{Cvoid}, (Ptr{Cvoid}, Ptr{Cvoid}, UInt),
15681565
ptr, a, nba)
15691566
# copy selector bytes
15701567
ccall(:memcpy, Ptr{Cvoid}, (Ptr{Cvoid}, Ptr{Cvoid}, UInt),
15711568
selptr, ccall(:jl_array_typetagdata, Ptr{UInt8}, (Any,), a), na)
15721569
selptr += na
1570+
elseif allocatedinline(T)
1571+
ccall(:memcpy, Ptr{Cvoid}, (Ptr{Cvoid}, Ptr{Cvoid}, UInt),
1572+
ptr, a, nba)
15731573
else
15741574
ccall(:jl_array_ptr_copy, Cvoid, (Any, Ptr{Cvoid}, Any, Ptr{Cvoid}, Int),
15751575
arr, ptr, a, pointer(a), na)

base/arraymath.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ function reverse(A::Array{T}; dims::Integer) where T
9494
end
9595
end
9696
else
97-
if isbitstype(T) && M>200
97+
if allocatedinline(T) && M>200
9898
for i = 1:sd
9999
ri = sd+1-i
100100
for j=0:stride:(N-stride)

src/datatype.c

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,7 @@ static unsigned union_isbits(jl_value_t *ty, size_t *nbytes, size_t *align) JL_N
248248
return 0;
249249
return na + nb;
250250
}
251-
if (jl_isbits(ty)) {
251+
if (jl_is_datatype(ty) && jl_datatype_isinlinealloc(ty)) {
252252
size_t sz = jl_datatype_size(ty);
253253
size_t al = jl_datatype_align(ty);
254254
if (*nbytes < sz)
@@ -292,6 +292,7 @@ static int references_name(jl_value_t *p, jl_typename_t *name) JL_NOTSAFEPOINT
292292
void jl_compute_field_offsets(jl_datatype_t *st)
293293
{
294294
size_t sz = 0, alignm = 1;
295+
size_t fldsz = 0, fldal = 0;
295296
int homogeneous = 1;
296297
jl_value_t *lastty = NULL;
297298
uint64_t max_offset = (((uint64_t)1) << 32) - 1;
@@ -302,12 +303,14 @@ void jl_compute_field_offsets(jl_datatype_t *st)
302303
// compute whether this type can be inlined
303304
// based on whether its definition is self-referential
304305
if (w->types != NULL) {
305-
st->isbitstype = st->isconcretetype && !st->mutabl;
306+
st->isbitstype = st->isinlinealloc = st->isconcretetype && !st->mutabl;
306307
size_t i, nf = jl_svec_len(st->types);
307308
for (i = 0; i < nf; i++) {
308309
jl_value_t *fld = jl_svecref(st->types, i);
309310
if (st->isbitstype)
310311
st->isbitstype = jl_is_datatype(fld) && ((jl_datatype_t*)fld)->isbitstype;
312+
if (st->isinlinealloc)
313+
st->isinlinealloc = (jl_is_datatype(fld) && ((jl_datatype_t*)fld)->isbitstype) || jl_islayout_inline(fld, &fldsz, &fldal);
311314
if (!st->zeroinit)
312315
st->zeroinit = (jl_is_datatype(fld) && ((jl_datatype_t*)fld)->isinlinealloc) ? ((jl_datatype_t*)fld)->zeroinit : 1;
313316
if (i < st->ninitialized) {
@@ -317,8 +320,7 @@ void jl_compute_field_offsets(jl_datatype_t *st)
317320
st->has_concrete_subtype &= !jl_is_datatype(fld) || ((jl_datatype_t *)fld)->has_concrete_subtype;
318321
}
319322
}
320-
if (st->isbitstype) {
321-
st->isinlinealloc = 1;
323+
if (st->isinlinealloc) {
322324
size_t i, nf = jl_svec_len(w->types);
323325
for (i = 0; i < nf; i++) {
324326
jl_value_t *fld = jl_svecref(w->types, i);

src/julia.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -931,6 +931,7 @@ STATIC_INLINE jl_value_t *jl_field_type_concrete(jl_datatype_t *st JL_PROPAGATES
931931
#define jl_datatype_align(t) (((jl_datatype_t*)t)->layout->alignment)
932932
#define jl_datatype_nbits(t) ((((jl_datatype_t*)t)->size)*8)
933933
#define jl_datatype_nfields(t) (((jl_datatype_t*)(t))->layout->nfields)
934+
#define jl_datatype_isinlinealloc(t) (((jl_datatype_t *)(t))->isinlinealloc)
934935

935936
// inline version with strong type check to detect typos in a `->name` chain
936937
STATIC_INLINE char *jl_symbol_name_(jl_sym_t *s) JL_NOTSAFEPOINT

test/core.jl

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5852,9 +5852,9 @@ let
58525852
b3 = B23367[b][1] # copy b via array assignment
58535853
addr(@nospecialize x) = ccall(:jl_value_ptr, Ptr{Cvoid}, (Any,), x)
58545854
@test addr(b) == addr(b)
5855-
@test addr(b) == addr(b2)
5856-
@test addr(b) == addr(b3)
5857-
@test addr(b2) == addr(b3)
5855+
# @test addr(b) == addr(b2)
5856+
# @test addr(b) == addr(b3)
5857+
# @test addr(b2) == addr(b3)
58585858

58595859
@test b === b2 === b3 === b
58605860
@test egal(b, b2) && egal(b2, b3) && egal(b3, b)
@@ -5863,7 +5863,7 @@ let
58635863
@test b.x === Int8(91)
58645864
@test b.z === Int8(23)
58655865
@test b.y === A23367((Int8(1), Int8(2), Int8(3), Int8(4), Int8(5), Int8(6), Int8(7)))
5866-
@test sizeof(b) == sizeof(Int) * 3
5866+
@test sizeof(b) == 12
58675867
@test A23367(Int8(1)).x === Int8(1)
58685868
@test A23367(Int8(0)).x === Int8(0)
58695869
@test A23367(Int16(1)).x === Int16(1)
@@ -5891,6 +5891,25 @@ for U in boxedunions
58915891
end
58925892
end
58935893

5894+
struct UnionFieldInlineStruct
5895+
x::Int64
5896+
y::Union{Float64, Missing}
5897+
end
5898+
5899+
@test sizeof(Vector{UnionFieldInlineStruct}(undef, 2)) == sizeof(UnionFieldInlineStruct) * 2
5900+
5901+
let x = UnionFieldInlineStruct(1, 3.14)
5902+
AInlineUnion = [x for i = 1:10]
5903+
@test sizeof(AInlineUnion) == sizeof(UnionFieldInlineStruct) * 10
5904+
BInlineUnion = Vector{UnionFieldInlineStruct}(undef, 10)
5905+
copyto!(BInlineUnion, AInlineUnion)
5906+
@test AInlineUnion == BInlineUnion
5907+
@test BInlineUnion[end] == x
5908+
CInlineUnion = vcat(AInlineUnion, BInlineUnion)
5909+
@test sizeof(CInlineUnion) == sizeof(UnionFieldInlineStruct) * 20
5910+
@test CInlineUnion[end] == x
5911+
end
5912+
58945913
# issue 31583
58955914
a31583 = "a"
58965915
f31583() = a31583 === "a"

0 commit comments

Comments
 (0)