Skip to content

type hashing is not folded for non-constant seed #58395

@matthias314

Description

@matthias314

In #52427 the hash of a type was changed to (CC @Seelengrab)

julia/base/hashing.jl

Lines 36 to 38 in 2a5cfee

# Types can't be deleted, so marking as total allows the compiler to look up the hash
hash(T::Type, h::UInt) =
hash((@assume_effects :total ccall(:jl_type_hash, UInt, (Any,), T)), h)

I would expect the call to jl_type_hash to be folded. But that doesn't always seem to be the case:

julia> f(h...) = hash(Char, h...);

julia> @code_typed f()  # OK
CodeInfo(
1 ─     return 0xaac71d196c645fea
) => UInt64

julia> @code_typed f(UInt(0))  # calls jl_type_hash
CodeInfo(
1 ─ %1  = Core.getfield(h, 1)::UInt64
│   %2  = Base.mul_int(0x0000000000000003, %1)::UInt64
│   %3  = $(Expr(:foreigncall, :(:jl_type_hash), UInt64, svec(Any), 0, :(:ccall), Char))::UInt64
│   %4  = Base.sub_int(%2, %3)::UInt64
│   %5  = Base.not_int(%4)::UInt64
│   %6  = Base.shl_int(%4, 0x0000000000000015)::UInt64
│   %7  = Base.add_int(%5, %6)::UInt64
│   %8  = Base.lshr_int(%7, 0x0000000000000018)::UInt64
│   %9  = Base.xor_int(%7, %8)::UInt64
│   %10 = Base.shl_int(%9, 0x0000000000000003)::UInt64
│   %11 = Base.shl_int(%9, 0x0000000000000008)::UInt64
│   %12 = Base.add_int(%9, %10)::UInt64
│   %13 = Base.add_int(%12, %11)::UInt64
│   %14 = Base.lshr_int(%13, 0x000000000000000e)::UInt64
│   %15 = Base.xor_int(%13, %14)::UInt64
│   %16 = Base.shl_int(%15, 0x0000000000000002)::UInt64
│   %17 = Base.shl_int(%15, 0x0000000000000004)::UInt64
│   %18 = Base.add_int(%15, %16)::UInt64
│   %19 = Base.add_int(%18, %17)::UInt64
│   %20 = Base.lshr_int(%19, 0x000000000000001c)::UInt64
│   %21 = Base.xor_int(%19, %20)::UInt64
│   %22 = Base.shl_int(%21, 0x000000000000001f)::UInt64
│   %23 = Base.add_int(%21, %22)::UInt64
└──       return %23
) => UInt64

If I compute the hash in stages, then it works as expected:

julia> typehash(::Type{T}, h::UInt = UInt(0)) where T = hash(hash(T), h);

julia> g(h...) = typehash(Char, h...);

julia> @code_typed g(UInt(0))
CodeInfo(
1 ─ %1 = Core.getfield(h, 1)::UInt64
│   %2 = Base.mul_int(0x0000000000000003, %1)::UInt64
│   %3 = Base.sub_int(0xc049c602eaefb696, %2)::UInt64
└──      return %3
) => UInt64
Julia Version 1.11.5
Commit 760b2e5b739 (2025-04-14 06:53 UTC)
Build Info:
  Official https://julialang.org/ release
Platform Info:
  OS: Linux (x86_64-linux-gnu)
  CPU: 4 × Intel(R) Core(TM) i3-10110U CPU @ 2.10GHz
  WORD_SIZE: 64
  LLVM: libLLVM-16.0.6 (ORCJIT, skylake)
Threads: 1 default, 0 interactive, 1 GC (on 4 virtual cores)

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions