Skip to content

Commit 4a2830a

Browse files
KenoJeffBezanson
authored andcommitted
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 (cherry picked from commit 004cb25)
1 parent 2567aaf commit 4a2830a

File tree

5 files changed

+79
-36
lines changed

5 files changed

+79
-36
lines changed

base/array.jl

Lines changed: 3 additions & 5 deletions
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

Lines changed: 41 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -306,16 +306,34 @@ function sizeof_nothrow(@nospecialize(x))
306306
end
307307
elseif isa(x, Conditional)
308308
return true
309-
else
310-
x = widenconst(x)
311309
end
312310
if isa(x, Union)
313311
return sizeof_nothrow(x.a) && sizeof_nothrow(x.b)
314312
end
315-
isconstType(x) && (x = x.parameters[1]) # since sizeof(typeof(x)) == sizeof(x)
316-
x === DataType && return false
317-
return isconcretetype(x) || isprimitivetype(x)
313+
t, exact = instanceof_tfunc(x)
314+
if !exact
315+
# Could always be bottom at runtime, which throws
316+
return false
317+
end
318+
if t !== Bottom
319+
t === DataType && return true
320+
x = t
321+
x = unwrap_unionall(x)
322+
if isa(x, Union)
323+
isinline, sz, _ = uniontype_layout(x)
324+
return isinline
325+
end
326+
isa(x, DataType) || return false
327+
x.layout == C_NULL && return false
328+
(datatype_nfields(x) == 0 && !datatype_pointerfree(x)) && return false
329+
return true
330+
else
331+
x = widenconst(x)
332+
x === DataType && return false
333+
return isconcretetype(x) || isprimitivetype(x)
334+
end
318335
end
336+
319337
function _const_sizeof(@nospecialize(x))
320338
# Constant Vector does not have constant size
321339
isa(x, Vector) && return Int
@@ -334,12 +352,27 @@ function sizeof_tfunc(@nospecialize(x),)
334352
isa(x, Const) && return _const_sizeof(x.val)
335353
isa(x, Conditional) && return _const_sizeof(Bool)
336354
isconstType(x) && return _const_sizeof(x.parameters[1])
337-
x = widenconst(x)
338355
if isa(x, Union)
339356
return tmerge(sizeof_tfunc(x.a), sizeof_tfunc(x.b))
340357
end
341-
x !== DataType && isconcretetype(x) && return _const_sizeof(x)
342-
isprimitivetype(x) && return _const_sizeof(x)
358+
# Core.sizeof operates on either a type or a value. First check which
359+
# case we're in.
360+
t, exact = instanceof_tfunc(x)
361+
if t !== Bottom
362+
# The value corresponding to `x` at runtime could be a type.
363+
# Normalize the query to ask about that type.
364+
x = unwrap_unionall(t)
365+
if isa(x, Union)
366+
isinline, sz, _ = uniontype_layout(x)
367+
return isinline ? Const(Int(sz)) : (exact ? Bottom : Int)
368+
end
369+
isa(x, DataType) || return Int
370+
(isconcretetype(x) || isprimitivetype(x)) && return _const_sizeof(x)
371+
else
372+
x = widenconst(x)
373+
x !== DataType && isconcretetype(x) && return _const_sizeof(x)
374+
isprimitivetype(x) && return _const_sizeof(x)
375+
end
343376
return Int
344377
end
345378
add_tfunc(Core.sizeof, 1, 1, sizeof_tfunc, 0)

base/reflection.jl

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -338,15 +338,19 @@ function datatype_alignment(dt::DataType)
338338
return Int(alignment)
339339
end
340340

341+
function uniontype_layout(T::Type)
342+
sz = RefValue{Csize_t}(0)
343+
algn = RefValue{Csize_t}(0)
344+
isinline = ccall(:jl_islayout_inline, Cint, (Any, Ptr{Csize_t}, Ptr{Csize_t}), T, sz, algn) != 0
345+
(isinline, sz[], algn[])
346+
end
347+
341348
# amount of total space taken by T when stored in a container
342349
function aligned_sizeof(T)
343350
@_pure_meta
344351
if isbitsunion(T)
345-
sz = Ref{Csize_t}(0)
346-
algn = Ref{Csize_t}(0)
347-
ccall(:jl_islayout_inline, Cint, (Any, Ptr{Csize_t}, Ptr{Csize_t}), T, sz, algn)
348-
al = algn[]
349-
return (sz[] + al - 1) & -al
352+
_, sz, al = uniontype_layout(T)
353+
return (sz + al - 1) & -al
350354
elseif allocatedinline(T)
351355
al = datatype_alignment(T)
352356
return (Core.sizeof(T) + al - 1) & -al
@@ -372,6 +376,19 @@ function datatype_haspadding(dt::DataType)
372376
return flags & 1 == 1
373377
end
374378

379+
"""
380+
Base.datatype_nfields(dt::DataType) -> Bool
381+
382+
Return the number of fields known to this datatype's layout.
383+
Can be called on any `isconcretetype`.
384+
"""
385+
function datatype_nfields(dt::DataType)
386+
@_pure_meta
387+
dt.layout == C_NULL && throw(UndefRefError())
388+
return unsafe_load(convert(Ptr{DataTypeLayout}, dt.layout)).nfields
389+
end
390+
391+
375392
"""
376393
Base.datatype_pointerfree(dt::DataType) -> Bool
377394

src/codegen.cpp

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2510,21 +2510,6 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f,
25102510
*ret = mark_julia_type(ctx, ctx.builder.CreateMul(len, elsize), false, jl_long_type);
25112511
return true;
25122512
}
2513-
if (jl_is_type_type((jl_value_t*)sty) && !jl_is_typevar(jl_tparam0(sty))) {
2514-
sty = (jl_datatype_t*)jl_tparam0(sty);
2515-
}
2516-
if (jl_is_datatype(sty) && sty != jl_symbol_type &&
2517-
sty->name != jl_array_typename &&
2518-
sty != jl_simplevector_type && sty != jl_string_type &&
2519-
// exclude DataType, since each DataType has its own size, not sizeof(DataType).
2520-
// this is issue #8798
2521-
sty != jl_datatype_type) {
2522-
if (jl_is_concrete_type((jl_value_t*)sty) ||
2523-
(jl_field_names(sty) == jl_emptysvec && jl_datatype_size(sty) > 0)) {
2524-
*ret = mark_julia_type(ctx, ConstantInt::get(T_size, jl_datatype_size(sty)), false, jl_long_type);
2525-
return true;
2526-
}
2527-
}
25282513
}
25292514

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

test/compiler/inference.jl

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1388,12 +1388,17 @@ end
13881388

13891389
using Core.Compiler: PartialStruct, nfields_tfunc, sizeof_tfunc, sizeof_nothrow
13901390
@test sizeof_tfunc(Const(Ptr)) === sizeof_tfunc(Union{Ptr, Int, Type{Ptr{Int8}}, Type{Int}}) === Const(Sys.WORD_SIZE ÷ 8)
1391-
@test sizeof_tfunc(Type{Ptr}) === Int
1391+
@test sizeof_tfunc(Type{Ptr}) === Const(sizeof(Ptr))
13921392
@test sizeof_nothrow(Union{Ptr, Int, Type{Ptr{Int8}}, Type{Int}})
13931393
@test sizeof_nothrow(Const(Ptr))
1394-
@test !sizeof_nothrow(Type{Ptr})
1395-
@test !sizeof_nothrow(Type{Union{Ptr{Int}, Int}})
1394+
@test sizeof_nothrow(Type{Ptr})
1395+
@test sizeof_nothrow(Type{Union{Ptr{Int}, Int}})
13961396
@test !sizeof_nothrow(Const(Tuple))
1397+
@test !sizeof_nothrow(Type{Vector{Int}})
1398+
@test !sizeof_nothrow(Type{Union{Int, String}})
1399+
@test sizeof_nothrow(String)
1400+
@test !sizeof_nothrow(Type{String})
1401+
@test sizeof_tfunc(Type{Union{Int64, Int32}}) == Const(Core.sizeof(Union{Int64, Int32}))
13971402
let PT = PartialStruct(Tuple{Int64,UInt64}, Any[Const(10, false), UInt64])
13981403
@test sizeof_tfunc(PT) === Const(16)
13991404
@test nfields_tfunc(PT) === Const(2)
@@ -2607,3 +2612,8 @@ _construct_structwithsplatnew() = StructWithSplatNew(("",))
26072612
f36531(args...) = tuple((args...)...)
26082613
@test @inferred(f36531(1,2,3)) == (1,2,3)
26092614
@test code_typed(f36531, Tuple{Vararg{Int}}) isa Vector
2615+
2616+
# Issue #36710 - sizeof(::UnionAll) tfunc correctness
2617+
@test (sizeof(Ptr),) == sizeof.((Ptr,)) == sizeof.((Ptr{Cvoid},))
2618+
@test Core.Compiler.sizeof_tfunc(UnionAll) === Int
2619+
@test !Core.Compiler.sizeof_nothrow(UnionAll)

0 commit comments

Comments
 (0)