Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 26 additions & 18 deletions Compiler/src/tfuncs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -524,11 +524,12 @@ function sizeof_nothrow(@nospecialize(x))
return true
end

function _const_sizeof(@nospecialize(x))
# f shall be Core.sizeof or Core.bitsizeof
function _const_sizeof(@nospecialize(f), @nospecialize(x))
# Constant GenericMemory does not have constant size
isa(x, GenericMemory) && return Int
size = try
Core.sizeof(x)
f(x)
catch ex
# Might return
# "argument is an abstract type; size is indeterminate" or
Expand All @@ -538,37 +539,40 @@ function _const_sizeof(@nospecialize(x))
end
return Const(size)
end
@nospecs function sizeof_tfunc(𝕃::AbstractLattice, x)
@nospecs function size_tfunc(𝕃::AbstractLattice, x, f)
x = widenmustalias(x)
isa(x, Const) && return _const_sizeof(x.val)
isa(x, Conditional) && return _const_sizeof(Bool)
isconstType(x) && return _const_sizeof(type_parameter(x))
isa(x, Const) && return _const_sizeof(f, x.val)
isa(x, Conditional) && return _const_sizeof(f, Bool)
isconstType(x) && return _const_sizeof(f, type_parameter(x))
xu = unwrap_unionall(x)
if isa(xu, Union)
return tmerge(sizeof_tfunc(𝕃, rewrap_unionall(xu.a, x)),
sizeof_tfunc(𝕃, rewrap_unionall(xu.b, x)))
return tmerge(size_tfunc(𝕃, rewrap_unionall(xu.a, x), f),
size_tfunc(𝕃, rewrap_unionall(xu.b, x), f))
end
# Core.sizeof operates on either a type or a value. First check which
# case we're in.
# Core.sizeof or Core.bitsizeof operate on either a type or a value.
# First check which case we're in.
t, exact = instanceof_tfunc(x, false)
if t !== Bottom
# The value corresponding to `x` at runtime could be a type.
# Normalize the query to ask about that type.
x = unwrap_unionall(t)
if exact && isa(x, Union)
isinline = uniontype_layout(x)[1]
return isinline ? Const(Int(Core.sizeof(x))) : Bottom
return isinline ? Const(Int(f(x))) : Bottom
end
isa(x, DataType) || return Int
(isconcretetype(x) || isprimitivetype(x)) && return _const_sizeof(x)
(isconcretetype(x) || isprimitivetype(x)) && return _const_sizeof(f, x)
else
x = widenconst(x)
x !== DataType && isconcretetype(x) && return _const_sizeof(x)
isprimitivetype(x) && return _const_sizeof(x)
x !== DataType && isconcretetype(x) && return _const_sizeof(f, x)
isprimitivetype(x) && return _const_sizeof(f, x)
end
return Int
end
@nospecs sizeof_tfunc(𝕃::AbstractLattice, x) = size_tfunc(𝕃, x, Core.sizeof)
@nospecs bitsizeof_tfunc(𝕃::AbstractLattice, x) = size_tfunc(𝕃, x, Core.bitsizeof)
add_tfunc(Core.sizeof, 1, 1, sizeof_tfunc, 1)
add_tfunc(Core.bitsizeof, 1, 1, bitsizeof_tfunc, 1)
@nospecs function nfields_tfunc(𝕃::AbstractLattice, x)
isa(x, Const) && return Const(nfields(x.val))
isa(x, Conditional) && return Const(0)
Expand Down Expand Up @@ -2580,7 +2584,7 @@ function _builtin_nothrow(𝕃::AbstractLattice, @nospecialize(f::Builtin), argt
return subtype_nothrow(𝕃, argtypes[1], argtypes[2])
elseif f === isdefined
return isdefined_nothrow(𝕃, argtypes)
elseif f === Core.sizeof
elseif f === Core.sizeof || f === Core.bitsizeof
na == 1 || return false
return sizeof_nothrow(argtypes[1])
elseif f === Core.ifelse
Expand Down Expand Up @@ -2632,6 +2636,7 @@ const _CONSISTENT_BUILTINS = Any[
apply_type,
isa,
UnionAll,
Core.bitsizeof,
Core.sizeof,
Core.ifelse,
(<:),
Expand Down Expand Up @@ -2659,6 +2664,7 @@ const _EFFECT_FREE_BUILTINS = [
memoryrefget,
memoryref_isassigned,
isdefined,
Core.bitsizeof,
Core.sizeof,
Core.ifelse,
Core._typevar,
Expand All @@ -2676,6 +2682,7 @@ const _INACCESSIBLEMEM_BUILTINS = Any[
(<:),
(===),
apply_type,
Core.bitsizeof,
Core.ifelse,
Core.sizeof,
svec,
Expand Down Expand Up @@ -2890,6 +2897,7 @@ const _EFFECTS_KNOWN_BUILTINS = Any[
# Core.memoryrefsetonce!,
# Core.memoryrefswap!,
memoryrefunset!,
Core.bitsizeof,
Core.sizeof,
svec,
Core.throw_methoderror,
Expand Down Expand Up @@ -3252,7 +3260,7 @@ function intrinsic_exct(𝕃::AbstractLattice, f::IntrinsicFunction, argtypes::V
if !isconcrete
return Union{ErrorException, TypeError}
end
if !(isprimitivetype(ty) && isprimitivetype(xty) && Core.sizeof(ty) === Core.sizeof(xty))
if !(isprimitivetype(ty) && isprimitivetype(xty) && Core.bitsizeof(ty) === Core.bitsizeof(xty))
return ErrorException
end
return Union{}
Expand All @@ -3278,14 +3286,14 @@ function intrinsic_exct(𝕃::AbstractLattice, f::IntrinsicFunction, argtypes::V
!(ty <: CORE_FLOAT_TYPES && xty <: CORE_FLOAT_TYPES && Core.sizeof(ty) > Core.sizeof(xty))
return ErrorException
end
if (f === Intrinsics.sext_int || f === Intrinsics.zext_int) && !(Core.sizeof(ty) > Core.sizeof(xty))
if (f === Intrinsics.sext_int || f === Intrinsics.zext_int) && !(Core.bitsizeof(ty) > Core.bitsizeof(xty))
return ErrorException
end
if f === Intrinsics.fptrunc &&
!(ty <: CORE_FLOAT_TYPES && xty <: CORE_FLOAT_TYPES && Core.sizeof(ty) < Core.sizeof(xty))
return ErrorException
end
if f === Intrinsics.trunc_int && !(Core.sizeof(ty) < Core.sizeof(xty))
if f === Intrinsics.trunc_int && !(Core.bitsizeof(ty) < Core.bitsizeof(xty))
return ErrorException
end
if (f === Intrinsics.fptoui || f === Intrinsics.fptosi) && !(xty <: CORE_FLOAT_TYPES)
Expand Down
27 changes: 27 additions & 0 deletions Compiler/test/effects.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1507,6 +1507,12 @@ end
@test Base.infer_effects(invokelatest, Tuple{Vararg{Any}}) == Compiler.Effects()
@test Base.infer_effects(invoke, Tuple{Vararg{Any}}) == Compiler.Effects()

bitsizeof_int() = Core.bitsizeof(Int)
let effects = Base.infer_effects(bitsizeof_int)
@test Compiler.is_foldable_nothrow(effects)
@test Compiler.is_inaccessiblememonly(effects)
end

# Core._svec_ref effects modeling (required for external abstract interpreter that doesn't run optimization)
let effects = Base.infer_effects((Core.SimpleVector,Int); optimize=false) do svec, i
Core._svec_ref(svec, i)
Expand Down Expand Up @@ -1553,6 +1559,27 @@ let f = (x) -> Core.Intrinsics.trunc_int(Int16, x)
@test catch_error_61435(f, Int16(0)) === :caught
end

# Intrinsic width checks use logical primitive widths rather than storage sizes.
primitive type EffectsUInt17 17 end
primitive type EffectsUInt23 23 end
let f = (x) -> Core.Intrinsics.bitcast(EffectsUInt17, x)
@test !Compiler.is_nothrow(Base.infer_effects(f, (EffectsUInt23,)))
@test Base.infer_exception_type(f, (EffectsUInt23,)) === ErrorException
@test !fully_eliminated((EffectsUInt23,)) do x
f(x)
return nothing
end
end
let f = (x) -> Core.Intrinsics.zext_int(EffectsUInt23, x)
@test Compiler.is_nothrow(Base.infer_effects(f, (EffectsUInt17,)))
end
let f = (x) -> Core.Intrinsics.sext_int(EffectsUInt23, x)
@test Compiler.is_nothrow(Base.infer_effects(f, (EffectsUInt17,)))
end
let f = (x) -> Core.Intrinsics.trunc_int(EffectsUInt17, x)
@test Compiler.is_nothrow(Base.infer_effects(f, (EffectsUInt23,)))
end

# issue #57324
module Issue57324
struct T <: AbstractVector{Float64}
Expand Down
14 changes: 14 additions & 0 deletions Compiler/test/inference.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1449,6 +1449,7 @@ test_const_return(()->sizeof(1 < 2), Tuple{}, 1)
test_const_return(()->fieldtype(Dict{Int64,Nothing}, :age), Tuple{}, UInt)
test_const_return(@eval(()->Core.sizeof($(Array{Int,0}(undef)))), Tuple{}, 2 * sizeof(Int))
test_const_return(@eval(()->Core.sizeof($(Matrix{Float32}(undef, 2, 2)))), Tuple{}, 4 * sizeof(Int))
primitive type BitsizeofUInt17 17 end
# TODO: do we want to implement these?
# test_const_return(@eval(()->sizeof($(Array{Int,0}(undef)))), Tuple{}, sizeof(Int))
# test_const_return(@eval(()->sizeof($(Matrix{Float32}(undef, 2, 2)))), Tuple{}, 4 * 2 * 2)
Expand All @@ -1460,6 +1461,15 @@ function sizeof_typeref(typeref)
end
@test @inferred(sizeof_typeref(Ref{DataType}(Int))) == sizeof(Int)
@test find_call(only(code_typed(sizeof_typeref, (Ref{DataType},)))[1], Core.sizeof, 2)
# Make sure Core.bitsizeof with a ::DataType as inferred input type is inferred but not constant.
function bitsizeof_typeref(typeref)
return Core.bitsizeof(typeref[])
end
@test bitsizeof_typeref(Ref{DataType}(BitsizeofUInt17)) == 17
let (src, rt) = only(code_typed(bitsizeof_typeref, (Ref{DataType},)))
@test rt === Int
@test find_call(src, Core.bitsizeof, 2)
end
# Constant `Vector` can be resized and shouldn't be optimized to a constant.
const constvec = [1, 2, 3]
@eval function sizeof_constvec()
Expand Down Expand Up @@ -1642,10 +1652,14 @@ let nfields_tfunc(@nospecialize xs...) =
Compiler.nfields_tfunc(Compiler.fallback_lattice, xs...)
sizeof_tfunc(@nospecialize xs...) =
Compiler.sizeof_tfunc(Compiler.fallback_lattice, xs...)
bitsizeof_tfunc(@nospecialize xs...) =
Compiler.bitsizeof_tfunc(Compiler.fallback_lattice, xs...)
sizeof_nothrow(@nospecialize xs...) =
Compiler.sizeof_nothrow(xs...)
@test sizeof_tfunc(Const(Ptr)) === sizeof_tfunc(Union{Ptr, Int, Type{Ptr{Int8}}, Type{Int}}) === Const(Sys.WORD_SIZE ÷ 8)
@test sizeof_tfunc(Type{Ptr}) === Const(sizeof(Ptr))
@test bitsizeof_tfunc(Type{BitsizeofUInt17}) === Const(17)
@test bitsizeof_tfunc(DataType) === Int
@test !sizeof_nothrow(Union{Ptr, Int, Type{Ptr{Int8}}, Type{Int}})
@test sizeof_nothrow(Union{Ptr, Int, Core.TypeEgal{Ptr{Int8}}, Core.TypeEgal{Int}})
@test sizeof_nothrow(Const(Ptr))
Expand Down
26 changes: 26 additions & 0 deletions base/essentials.jl
Original file line number Diff line number Diff line change
Expand Up @@ -834,6 +834,32 @@ Stacktrace:
"""
sizeof(x) = Core.sizeof(x)

"""
Core.bitsizeof(T::DataType)
Core.bitsizeof(obj)

Logical size, in bits, of the canonical binary representation of the given `DataType` `T`, if any.
Or the logical size, in bits, of object `obj` if it is not a `DataType`.

For primitive types, this may differ from `8*sizeof(T)` when the type uses byte-rounded storage
with unused bits in the last byte.

# Examples
```jldoctest
Comment thread
fingolfin marked this conversation as resolved.
julia> Core.bitsizeof(Float32)
32

julia> Core.bitsizeof(1.0)
64

julia> primitive type MyUInt63 <: Unsigned 63 end

julia> Core.bitsizeof(MyUInt63)
63
```
"""
Core.bitsizeof

"""
ifelse(condition::Bool, x, y)

Expand Down
12 changes: 10 additions & 2 deletions base/intfuncs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1139,13 +1139,15 @@ julia> bitstring(2.2)
"""
function bitstring(x::T) where {T}
isprimitivetype(T) || throw(ArgumentError(LazyString(T, " not a primitive type")))
sz = sizeof(T) * 8
sz = Core.bitsizeof(T)
onebyte = sz == 8
subbyte = sz < 8
str = _string_n(sz)
GC.@preserve str begin
p = pointer(str)
i = sz
while i >= 4
b = UInt32(sizeof(T) == 1 ? bitcast(UInt8, x) : trunc_int(UInt8, x))
b = UInt32(onebyte ? bitcast(UInt8, x) : subbyte ? zext_int(UInt8, x) : trunc_int(UInt8, x))
d = 0x30303030 +% ((b *% 0x08040201) >> 0x3) & 0x01010101
unsafe_store!(p, (d >> 0x00) % UInt8, i-3)
unsafe_store!(p, (d >> 0x08) % UInt8, i-2)
Expand All @@ -1154,6 +1156,12 @@ function bitstring(x::T) where {T}
x = lshr_int(x, 4)
i -= 4
end
while i > 0
b = UInt8(onebyte ? bitcast(UInt8, x) : subbyte ? zext_int(UInt8, x) : trunc_int(UInt8, x))
unsafe_store!(p, 0x30 + (b & 0x01), i)
x = lshr_int(x, 1)
i -= 1
end
end
return str
end
Expand Down
30 changes: 29 additions & 1 deletion base/reinterpretarray.jl
Original file line number Diff line number Diff line change
Expand Up @@ -768,6 +768,11 @@ end
"""
@assume_effects :foldable function padding(T::DataType, baseoffset::Int = 0)
pads = Padding[]
if isprimitivetype(T)
Core.bitsizeof(T) % 8 == 0 || throw(ArgumentError(LazyString(
"padding cannot be computed for non-byte-aligned primitive type ", T)))
return Core.svec()
end
last_end::Int = baseoffset
for i = 1:fieldcount(T)
offset = baseoffset + Int(fieldoffset(T, i))
Expand Down Expand Up @@ -827,11 +832,30 @@ end
end

@assume_effects :foldable function packedsize(::Type{T}) where T
if isprimitivetype(T)
Core.bitsizeof(T) % 8 == 0 || throw(ArgumentError(LazyString(
"packed size cannot be computed for non-byte-aligned primitive type ", T)))
return Core.bitsizeof(T) >> 3
end
pads = padding(T)
return sizeof(T) - sum((p.size for p ∈ pads), init = 0)
end

@assume_effects :foldable ispacked(::Type{T}) where T = isempty(padding(T))
@assume_effects :foldable function ispacked(::Type{T}) where T
isprimitivetype(T) && return Core.bitsizeof(T) == sizeof(T) * 8
return isempty(padding(T))
end

@assume_effects :foldable function has_bit_padding(::Type{T}) where T
if isprimitivetype(T)
return Core.bitsizeof(T) % 8 != 0
end
T isa Union && return any(has_bit_padding, uniontypes(T))
for i in 1:fieldcount(T)
has_bit_padding(fieldtype(T, i)) && return true
end
return false
end

function _copytopacked!(ptr_out::Ptr{Out}, ptr_in::Ptr{In}) where {Out, In}
writeoffset = 0
Expand Down Expand Up @@ -869,6 +893,10 @@ end
# handle non-primitive types
isbitstype(Out) || throw(ArgumentError("Target type for `reinterpret` must be isbits"))
isbitstype(In) || throw(ArgumentError("Source type for `reinterpret` must be isbits"))
has_bit_padding(Out) && throw(ArgumentError(LazyString(
"cannot `reinterpret` type ", Out, " containing non-byte-aligned primitive fields")))
has_bit_padding(In) && throw(ArgumentError(LazyString(
"cannot `reinterpret` type ", In, " containing non-byte-aligned primitive fields")))
inpackedsize = packedsize(In)
outpackedsize = packedsize(Out)
inpackedsize == outpackedsize ||
Expand Down
3 changes: 2 additions & 1 deletion base/runtime_internals.jl
Original file line number Diff line number Diff line change
Expand Up @@ -617,7 +617,8 @@ struct DataTypeLayout
# arrayelem_isatomic : 1;
# arrayelem_islocked : 1;
# isbitsegal : 1;
# padding : 8;
# unused_bits : 3;
# padding : 5;
end

function DataTypeLayout(dt::DataType)
Expand Down
14 changes: 11 additions & 3 deletions base/show.jl
Original file line number Diff line number Diff line change
Expand Up @@ -525,10 +525,18 @@ function _show_default(io::IO, @nospecialize(x))
else
print(io, "0x")
r = Ref{Any}(x)
nbits = Core.bitsizeof(t)
nbytes = cld(nbits, 8)
GC.@preserve r begin
p = unsafe_convert(Ptr{Cvoid}, r)
for i in (nb - 1):-1:0
print(io, string(unsafe_load(convert(Ptr{UInt8}, p + i)), base = 16, pad = 2))
for i in (nbytes - 1):-1:0
byte = unsafe_load(convert(Ptr{UInt8}, p + i))
if i == nbytes - 1 && nbits % 8 != 0
byte &= (UInt8(1) << (nbits % 8)) - UInt8(1)
print(io, string(byte, base = 16, pad = cld(nbits % 8, 4)))
else
print(io, string(byte, base = 16, pad = 2))
end
end
end
end
Expand Down Expand Up @@ -1374,7 +1382,7 @@ show(io::IO, ::Nothing) = print(io, "nothing")
show(io::IO, n::Signed) = (write(io, string(n)); nothing)
function show(io::IO, n::Unsigned)
if get(io, :hexunsigned, true)::Bool
print(io, "0x", string(n, pad = sizeof(n)<<1, base = 16))
print(io, "0x", string(n, pad = cld(Core.bitsizeof(n), 4), base = 16))
else
if get(io, :typeinfo, Nothing)::Type == typeof(n)
print(io, n)
Expand Down
Loading