Skip to content

Commit 004cb25

Browse files
authored
Fix correctness issues in sizeof tfunc (#36727)
The `Core.sizeof` function can take either a value or a type, which can be a bit confusing in the tfunc, because the tfunc operates on the types of the values passed to the builtin. In particular, we were incorrectly returning Const(16) (on 64bit) for sizeof_tfunc(UnionAll), because it was treating it the asme as `sizeof_tfunc(Const(UnionAll))`. Try to clean that up as well as fixing a similar confusion in the nothrow version of this function. Lastly, we had a similar fast path in codegen, which would try to inline the actual size for a constant datatype argument. For codegen, just rm that whole code path, since it should have no more information than inference and figuring it out in inference exposes strictly more optimization opportunities. Fixes #36710
1 parent 2cca0a4 commit 004cb25

File tree

5 files changed

+79
-36
lines changed

5 files changed

+79
-36
lines changed

base/array.jl

+3-5
Original file line numberDiff line numberDiff line change
@@ -211,11 +211,9 @@ julia> Base.bitsunionsize(Union{Float64, UInt8, Int128})
211211
```
212212
"""
213213
function bitsunionsize(u::Union)
214-
sz = Ref{Csize_t}(0)
215-
algn = Ref{Csize_t}(0)
216-
isunboxed = ccall(:jl_islayout_inline, Cint, (Any, Ptr{Csize_t}, Ptr{Csize_t}), u, sz, algn)
217-
@assert isunboxed != Cint(0)
218-
return sz[]
214+
isinline, sz, _ = uniontype_layout(u)
215+
@assert isinline
216+
return sz
219217
end
220218

221219
length(a::Array) = arraylen(a)

base/compiler/tfuncs.jl

+41-8
Original file line numberDiff line numberDiff line change
@@ -302,16 +302,34 @@ function sizeof_nothrow(@nospecialize(x))
302302
end
303303
elseif isa(x, Conditional)
304304
return true
305-
else
306-
x = widenconst(x)
307305
end
308306
if isa(x, Union)
309307
return sizeof_nothrow(x.a) && sizeof_nothrow(x.b)
310308
end
311-
isconstType(x) && (x = x.parameters[1]) # since sizeof(typeof(x)) == sizeof(x)
312-
x === DataType && return false
313-
return isconcretetype(x) || isprimitivetype(x)
309+
t, exact = instanceof_tfunc(x)
310+
if !exact
311+
# Could always be bottom at runtime, which throws
312+
return false
313+
end
314+
if t !== Bottom
315+
t === DataType && return true
316+
x = t
317+
x = unwrap_unionall(x)
318+
if isa(x, Union)
319+
isinline, sz, _ = uniontype_layout(x)
320+
return isinline
321+
end
322+
isa(x, DataType) || return false
323+
x.layout == C_NULL && return false
324+
(datatype_nfields(x) == 0 && !datatype_pointerfree(x)) && return false
325+
return true
326+
else
327+
x = widenconst(x)
328+
x === DataType && return false
329+
return isconcretetype(x) || isprimitivetype(x)
330+
end
314331
end
332+
315333
function _const_sizeof(@nospecialize(x))
316334
# Constant Vector does not have constant size
317335
isa(x, Vector) && return Int
@@ -330,12 +348,27 @@ function sizeof_tfunc(@nospecialize(x),)
330348
isa(x, Const) && return _const_sizeof(x.val)
331349
isa(x, Conditional) && return _const_sizeof(Bool)
332350
isconstType(x) && return _const_sizeof(x.parameters[1])
333-
x = widenconst(x)
334351
if isa(x, Union)
335352
return tmerge(sizeof_tfunc(x.a), sizeof_tfunc(x.b))
336353
end
337-
x !== DataType && isconcretetype(x) && return _const_sizeof(x)
338-
isprimitivetype(x) && return _const_sizeof(x)
354+
# Core.sizeof operates on either a type or a value. First check which
355+
# case we're in.
356+
t, exact = instanceof_tfunc(x)
357+
if t !== Bottom
358+
# The value corresponding to `x` at runtime could be a type.
359+
# Normalize the query to ask about that type.
360+
x = unwrap_unionall(t)
361+
if isa(x, Union)
362+
isinline, sz, _ = uniontype_layout(x)
363+
return isinline ? Const(Int(sz)) : (exact ? Bottom : Int)
364+
end
365+
isa(x, DataType) || return Int
366+
(isconcretetype(x) || isprimitivetype(x)) && return _const_sizeof(x)
367+
else
368+
x = widenconst(x)
369+
x !== DataType && isconcretetype(x) && return _const_sizeof(x)
370+
isprimitivetype(x) && return _const_sizeof(x)
371+
end
339372
return Int
340373
end
341374
add_tfunc(Core.sizeof, 1, 1, sizeof_tfunc, 1)

base/reflection.jl

+22-5
Original file line numberDiff line numberDiff line change
@@ -347,15 +347,19 @@ function datatype_alignment(dt::DataType)
347347
return Int(alignment)
348348
end
349349

350+
function uniontype_layout(T::Type)
351+
sz = RefValue{Csize_t}(0)
352+
algn = RefValue{Csize_t}(0)
353+
isinline = ccall(:jl_islayout_inline, Cint, (Any, Ptr{Csize_t}, Ptr{Csize_t}), T, sz, algn) != 0
354+
(isinline, sz[], algn[])
355+
end
356+
350357
# amount of total space taken by T when stored in a container
351358
function aligned_sizeof(T::Type)
352359
@_pure_meta
353360
if isbitsunion(T)
354-
sz = Ref{Csize_t}(0)
355-
algn = Ref{Csize_t}(0)
356-
ccall(:jl_islayout_inline, Cint, (Any, Ptr{Csize_t}, Ptr{Csize_t}), T, sz, algn)
357-
al = algn[]
358-
return (sz[] + al - 1) & -al
361+
_, sz, al = uniontype_layout(T)
362+
return (sz + al - 1) & -al
359363
elseif allocatedinline(T)
360364
al = datatype_alignment(T)
361365
return (Core.sizeof(T) + al - 1) & -al
@@ -381,6 +385,19 @@ function datatype_haspadding(dt::DataType)
381385
return flags & 1 == 1
382386
end
383387

388+
"""
389+
Base.datatype_nfields(dt::DataType) -> Bool
390+
391+
Return the number of fields known to this datatype's layout.
392+
Can be called on any `isconcretetype`.
393+
"""
394+
function datatype_nfields(dt::DataType)
395+
@_pure_meta
396+
dt.layout == C_NULL && throw(UndefRefError())
397+
return unsafe_load(convert(Ptr{DataTypeLayout}, dt.layout)).nfields
398+
end
399+
400+
384401
"""
385402
Base.datatype_pointerfree(dt::DataType) -> Bool
386403

src/codegen.cpp

-15
Original file line numberDiff line numberDiff line change
@@ -3039,21 +3039,6 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f,
30393039
*ret = mark_julia_type(ctx, ctx.builder.CreateMul(len, elsize), false, jl_long_type);
30403040
return true;
30413041
}
3042-
if (jl_is_type_type((jl_value_t*)sty) && !jl_is_typevar(jl_tparam0(sty))) {
3043-
sty = (jl_datatype_t*)jl_tparam0(sty);
3044-
}
3045-
if (jl_is_datatype(sty) && sty != jl_symbol_type &&
3046-
sty->name != jl_array_typename &&
3047-
sty != jl_simplevector_type && sty != jl_string_type &&
3048-
// exclude DataType, since each DataType has its own size, not sizeof(DataType).
3049-
// this is issue #8798
3050-
sty != jl_datatype_type) {
3051-
if (jl_is_concrete_type((jl_value_t*)sty) ||
3052-
(jl_field_names(sty) == jl_emptysvec && jl_datatype_size(sty) > 0)) {
3053-
*ret = mark_julia_type(ctx, ConstantInt::get(T_size, jl_datatype_size(sty)), false, jl_long_type);
3054-
return true;
3055-
}
3056-
}
30573042
}
30583043

30593044
else if (f == jl_builtin_apply_type && nargs > 0) {

test/compiler/inference.jl

+13-3
Original file line numberDiff line numberDiff line change
@@ -1400,12 +1400,17 @@ egal_conditional_lattice3(x, y) = x === y + y ? "" : 1
14001400

14011401
using Core.Compiler: PartialStruct, nfields_tfunc, sizeof_tfunc, sizeof_nothrow
14021402
@test sizeof_tfunc(Const(Ptr)) === sizeof_tfunc(Union{Ptr, Int, Type{Ptr{Int8}}, Type{Int}}) === Const(Sys.WORD_SIZE ÷ 8)
1403-
@test sizeof_tfunc(Type{Ptr}) === Int
1403+
@test sizeof_tfunc(Type{Ptr}) === Const(sizeof(Ptr))
14041404
@test sizeof_nothrow(Union{Ptr, Int, Type{Ptr{Int8}}, Type{Int}})
14051405
@test sizeof_nothrow(Const(Ptr))
1406-
@test !sizeof_nothrow(Type{Ptr})
1407-
@test !sizeof_nothrow(Type{Union{Ptr{Int}, Int}})
1406+
@test sizeof_nothrow(Type{Ptr})
1407+
@test sizeof_nothrow(Type{Union{Ptr{Int}, Int}})
14081408
@test !sizeof_nothrow(Const(Tuple))
1409+
@test !sizeof_nothrow(Type{Vector{Int}})
1410+
@test !sizeof_nothrow(Type{Union{Int, String}})
1411+
@test sizeof_nothrow(String)
1412+
@test !sizeof_nothrow(Type{String})
1413+
@test sizeof_tfunc(Type{Union{Int64, Int32}}) == Const(Core.sizeof(Union{Int64, Int32}))
14091414
let PT = PartialStruct(Tuple{Int64,UInt64}, Any[Const(10, false), UInt64])
14101415
@test sizeof_tfunc(PT) === Const(16)
14111416
@test nfields_tfunc(PT) === Const(2)
@@ -2734,3 +2739,8 @@ end
27342739

27352740
f_generator_splat(t::Tuple) = tuple((identity(l) for l in t)...)
27362741
@test Base.return_types(f_generator_splat, (Tuple{Symbol, Int64, Float64},)) == Any[Tuple{Symbol, Int64, Float64}]
2742+
2743+
# Issue #36710 - sizeof(::UnionAll) tfunc correctness
2744+
@test (sizeof(Ptr),) == sizeof.((Ptr,)) == sizeof.((Ptr{Cvoid},))
2745+
@test Core.Compiler.sizeof_tfunc(UnionAll) === Int
2746+
@test !Core.Compiler.sizeof_nothrow(UnionAll)

0 commit comments

Comments
 (0)