Skip to content

Commit

Permalink
Merge pull request #32448 from JuliaLang/jq/inlineunionfields
Browse files Browse the repository at this point in the history
For structs with all isbits or isbitsunion fields, allow them to be s…
  • Loading branch information
quinnj authored Aug 28, 2019
2 parents ef75269 + d94e2b5 commit 8093a7c
Show file tree
Hide file tree
Showing 6 changed files with 47 additions and 24 deletions.
1 change: 1 addition & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ Julia v1.4 Release Notes
New language features
---------------------

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

Language changes
----------------
Expand Down
30 changes: 15 additions & 15 deletions base/array.jl
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,8 @@ size(a::Array{<:Any,N}) where {N} = (@_inline_meta; ntuple(M -> size(a, M), Val(

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

allocatedinline(::Type{T}) where {T} = (@_pure_meta; ccall(:jl_array_store_unboxed, Cint, (Any,), T) != Cint(0))

"""
Base.isbitsunion(::Type{T})
Expand All @@ -172,14 +174,12 @@ julia> Base.isbitsunion(Union{Float64, String})
false
```
"""
isbitsunion(u::Union) = (@_pure_meta; ccall(:jl_array_store_unboxed, Cint, (Any,), u) != Cint(0))
isbitsunion(u::Union) = allocatedinline(u)
isbitsunion(x) = false

isptrelement(t::Type) = (@_pure_meta; ccall(:jl_array_store_unboxed, Cint, (Any,), t) == Cint(0))

function _unsetindex!(A::Array{T}, i::Int) where {T}
@boundscheck checkbounds(A, i)
if isptrelement(T)
if !allocatedinline(T)
t = @_gc_preserve_begin A
p = Ptr{Ptr{Cvoid}}(pointer(A))
unsafe_store!(p, C_NULL, i)
Expand Down Expand Up @@ -212,7 +212,7 @@ function bitsunionsize(u::Union)
end

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

function isassigned(a::Array, i::Int...)
Expand Down Expand Up @@ -255,16 +255,16 @@ the same manner as C.
function unsafe_copyto!(dest::Array{T}, doffs, src::Array{T}, soffs, n) where T
t1 = @_gc_preserve_begin dest
t2 = @_gc_preserve_begin src
if isbitstype(T)
unsafe_copyto!(pointer(dest, doffs), pointer(src, soffs), n)
elseif isbitsunion(T)
if isbitsunion(T)
ccall(:memmove, Ptr{Cvoid}, (Ptr{Cvoid}, Ptr{Cvoid}, UInt),
pointer(dest, doffs), pointer(src, soffs), n * Base.bitsunionsize(T))
# copy selector bytes
ccall(:memmove, Ptr{Cvoid}, (Ptr{Cvoid}, Ptr{Cvoid}, UInt),
ccall(:jl_array_typetagdata, Ptr{UInt8}, (Any,), dest) + doffs - 1,
ccall(:jl_array_typetagdata, Ptr{UInt8}, (Any,), src) + soffs - 1,
n)
elseif allocatedinline(T)
unsafe_copyto!(pointer(dest, doffs), pointer(src, soffs), n)
else
ccall(:jl_array_ptr_copy, Cvoid, (Any, Ptr{Cvoid}, Any, Ptr{Cvoid}, Int),
dest, pointer(dest, doffs), src, pointer(src, soffs), n)
Expand Down Expand Up @@ -1548,28 +1548,28 @@ function vcat(arrays::Vector{T}...) where T
end
arr = Vector{T}(undef, n)
ptr = pointer(arr)
if isbitstype(T)
elsz = Core.sizeof(T)
elseif isbitsunion(T)
if isbitsunion(T)
elsz = bitsunionsize(T)
selptr = ccall(:jl_array_typetagdata, Ptr{UInt8}, (Any,), arr)
elseif allocatedinline(T)
elsz = Core.sizeof(T)
else
elsz = Core.sizeof(Ptr{Cvoid})
end
t = @_gc_preserve_begin arr
for a in arrays
na = length(a)
nba = na * elsz
if isbitstype(T)
ccall(:memcpy, Ptr{Cvoid}, (Ptr{Cvoid}, Ptr{Cvoid}, UInt),
ptr, a, nba)
elseif isbitsunion(T)
if isbitsunion(T)
ccall(:memcpy, Ptr{Cvoid}, (Ptr{Cvoid}, Ptr{Cvoid}, UInt),
ptr, a, nba)
# copy selector bytes
ccall(:memcpy, Ptr{Cvoid}, (Ptr{Cvoid}, Ptr{Cvoid}, UInt),
selptr, ccall(:jl_array_typetagdata, Ptr{UInt8}, (Any,), a), na)
selptr += na
elseif allocatedinline(T)
ccall(:memcpy, Ptr{Cvoid}, (Ptr{Cvoid}, Ptr{Cvoid}, UInt),
ptr, a, nba)
else
ccall(:jl_array_ptr_copy, Cvoid, (Any, Ptr{Cvoid}, Any, Ptr{Cvoid}, Int),
arr, ptr, a, pointer(a), na)
Expand Down
2 changes: 1 addition & 1 deletion base/arraymath.jl
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ function reverse(A::Array{T}; dims::Integer) where T
end
end
else
if isbitstype(T) && M>200
if allocatedinline(T) && M>200
for i = 1:sd
ri = sd+1-i
for j=0:stride:(N-stride)
Expand Down
10 changes: 6 additions & 4 deletions src/datatype.c
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,7 @@ static unsigned union_isbits(jl_value_t *ty, size_t *nbytes, size_t *align) JL_N
return 0;
return na + nb;
}
if (jl_isbits(ty)) {
if (jl_is_datatype(ty) && jl_datatype_isinlinealloc(ty)) {
size_t sz = jl_datatype_size(ty);
size_t al = jl_datatype_align(ty);
if (*nbytes < sz)
Expand Down Expand Up @@ -292,6 +292,7 @@ static int references_name(jl_value_t *p, jl_typename_t *name) JL_NOTSAFEPOINT
void jl_compute_field_offsets(jl_datatype_t *st)
{
size_t sz = 0, alignm = 1;
size_t fldsz = 0, fldal = 0;
int homogeneous = 1;
jl_value_t *lastty = NULL;
uint64_t max_offset = (((uint64_t)1) << 32) - 1;
Expand All @@ -302,12 +303,14 @@ void jl_compute_field_offsets(jl_datatype_t *st)
// compute whether this type can be inlined
// based on whether its definition is self-referential
if (w->types != NULL) {
st->isbitstype = st->isconcretetype && !st->mutabl;
st->isbitstype = st->isinlinealloc = st->isconcretetype && !st->mutabl;
size_t i, nf = jl_svec_len(st->types);
for (i = 0; i < nf; i++) {
jl_value_t *fld = jl_svecref(st->types, i);
if (st->isbitstype)
st->isbitstype = jl_is_datatype(fld) && ((jl_datatype_t*)fld)->isbitstype;
if (st->isinlinealloc)
st->isinlinealloc = (jl_is_datatype(fld) && ((jl_datatype_t*)fld)->isbitstype) || jl_islayout_inline(fld, &fldsz, &fldal);
if (!st->zeroinit)
st->zeroinit = (jl_is_datatype(fld) && ((jl_datatype_t*)fld)->isinlinealloc) ? ((jl_datatype_t*)fld)->zeroinit : 1;
if (i < st->ninitialized) {
Expand All @@ -317,8 +320,7 @@ void jl_compute_field_offsets(jl_datatype_t *st)
st->has_concrete_subtype &= !jl_is_datatype(fld) || ((jl_datatype_t *)fld)->has_concrete_subtype;
}
}
if (st->isbitstype) {
st->isinlinealloc = 1;
if (st->isinlinealloc) {
size_t i, nf = jl_svec_len(w->types);
for (i = 0; i < nf; i++) {
jl_value_t *fld = jl_svecref(w->types, i);
Expand Down
1 change: 1 addition & 0 deletions src/julia.h
Original file line number Diff line number Diff line change
Expand Up @@ -931,6 +931,7 @@ STATIC_INLINE jl_value_t *jl_field_type_concrete(jl_datatype_t *st JL_PROPAGATES
#define jl_datatype_align(t) (((jl_datatype_t*)t)->layout->alignment)
#define jl_datatype_nbits(t) ((((jl_datatype_t*)t)->size)*8)
#define jl_datatype_nfields(t) (((jl_datatype_t*)(t))->layout->nfields)
#define jl_datatype_isinlinealloc(t) (((jl_datatype_t *)(t))->isinlinealloc)

// inline version with strong type check to detect typos in a `->name` chain
STATIC_INLINE char *jl_symbol_name_(jl_sym_t *s) JL_NOTSAFEPOINT
Expand Down
27 changes: 23 additions & 4 deletions test/core.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5852,9 +5852,9 @@ let
b3 = B23367[b][1] # copy b via array assignment
addr(@nospecialize x) = ccall(:jl_value_ptr, Ptr{Cvoid}, (Any,), x)
@test addr(b) == addr(b)
@test addr(b) == addr(b2)
@test addr(b) == addr(b3)
@test addr(b2) == addr(b3)
# @test addr(b) == addr(b2)
# @test addr(b) == addr(b3)
# @test addr(b2) == addr(b3)

@test b === b2 === b3 === b
@test egal(b, b2) && egal(b2, b3) && egal(b3, b)
Expand All @@ -5863,7 +5863,7 @@ let
@test b.x === Int8(91)
@test b.z === Int8(23)
@test b.y === A23367((Int8(1), Int8(2), Int8(3), Int8(4), Int8(5), Int8(6), Int8(7)))
@test sizeof(b) == sizeof(Int) * 3
@test sizeof(b) == 12
@test A23367(Int8(1)).x === Int8(1)
@test A23367(Int8(0)).x === Int8(0)
@test A23367(Int16(1)).x === Int16(1)
Expand Down Expand Up @@ -5891,6 +5891,25 @@ for U in boxedunions
end
end

struct UnionFieldInlineStruct
x::Int64
y::Union{Float64, Missing}
end

@test sizeof(Vector{UnionFieldInlineStruct}(undef, 2)) == sizeof(UnionFieldInlineStruct) * 2

let x = UnionFieldInlineStruct(1, 3.14)
AInlineUnion = [x for i = 1:10]
@test sizeof(AInlineUnion) == sizeof(UnionFieldInlineStruct) * 10
BInlineUnion = Vector{UnionFieldInlineStruct}(undef, 10)
copyto!(BInlineUnion, AInlineUnion)
@test AInlineUnion == BInlineUnion
@test BInlineUnion[end] == x
CInlineUnion = vcat(AInlineUnion, BInlineUnion)
@test sizeof(CInlineUnion) == sizeof(UnionFieldInlineStruct) * 20
@test CInlineUnion[end] == x
end

# issue 31583
a31583 = "a"
f31583() = a31583 === "a"
Expand Down

0 comments on commit 8093a7c

Please sign in to comment.