Skip to content
8 changes: 6 additions & 2 deletions base/hashing.jl
Original file line number Diff line number Diff line change
Expand Up @@ -236,17 +236,21 @@ end
## symbol & expression hashing ##
if UInt === UInt64
# conservatively hash using == equality of all of the data, even though == often uses === internally
hash(x::Expr, h::UInt) = hash(x.args, hash(x.head, h ⊻ 0x83c7900696d26dc6))
hash(x::QuoteNode, h::UInt) = hash(x.value, h ⊻ 0x2c97bf8b3de87020)
hash(x::PhiNode, h::UInt) = hash(x.edges, hash(x.values, h ⊻ 0x2c97bf8b3de87020))
hash(x::PhiCNode, h::UInt) = hash(x.values, h ⊻ 0x2c97bf8b3de87020)
else
hash(x::Expr, h::UInt) = hash(x.args, hash(x.head, h ⊻ 0x469d72af))
hash(x::QuoteNode, h::UInt) = hash(x.value, h ⊻ 0x469d72af)
hash(x::PhiNode, h::UInt) = hash(x.edges, hash(x.values, h ⊻ 0x469d72af))
hash(x::PhiCNode, h::UInt) = hash(x.values, h ⊻ 0x469d72af)
end

function hash(x::Expr, h::UInt)
h = hash(x.head, h ⊻ (UInt === UInt64 ? 0x83c7900696d26dc6 : 0x469d72af))
# Hint that `x.args::Vector{Any}` is mostly Expr, Symbol, and LineNumberNode.
hash_shaped(x.args, h ⊻ hash_abstractarray_seed, (Val{Expr}(), Val{Symbol}(), Val{LineNumberNode}()))
end

function hash(x::CodeInfo, h::UInt)
h ⊻= UInt === UInt64 ? 0x2c97bf8b3de87020 : 0x469d72af
for i in 1:nfields(x)
Expand Down
37 changes: 32 additions & 5 deletions base/multidimensional.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2043,7 +2043,34 @@ function _hash_fib(A, h::UInt)
return hash_uint(h)
end

function hash_shaped(A, h::UInt)
"""
union_split(f, x, ts::Tuple{Vararg{Val}}, args...)

call `f(x, args...)`, union-splitting on all the types specified by `ts`

`union_split(f, x, (Val{T1}(), Val{T2}()), y, z)` is equivalent to

```
if x isa T1
f(x, y, z)
elseif x isa T2
f(x, y, z)
else
f(x, y, z)
end
```
"""
@inline function union_split(f, @nospecialize(x), ts::Tuple{Val{T}, Vararg{Val,N}}, args...) where {T, N}
if x isa T
f(x, args...)
else
union_split(f, x, Base.tail(ts), args...)
end
end
@inline union_split(f, x, ::Tuple{}, args::Vararg{Any, N}) where {N} = f(x, args...)

function hash_shaped(A, h0::UInt, eltype_hint=())
h::UInt = h0
# Axes are themselves AbstractArrays, so hashing them directly would stack overflow
# Instead hash the tuple of firsts and lasts along each dimension
h = hash(map(first, axes(A)), h)
Expand All @@ -2053,20 +2080,20 @@ function hash_shaped(A, h::UInt)
if len < 8
# for the shortest arrays we chain directly
for elt in A
h = hash(elt, h)
h = union_split(hash, elt, eltype_hint, h)
end
return h
elseif len < 32768
# separate accumulator streams, unrolled
@nexprs 8 i -> p_i = h
@nexprs 8 i -> p_i::UInt = h
n = 1
limit = len - 7
while n <= limit
@nexprs 8 i -> p_i = hash(A[n + i - 1], p_i)
@nexprs 8 i -> p_i = union_split(hash, A[n + i - 1], eltype_hint, p_i)
n += 8
end
while n <= len
p_1 = hash(A[n], p_1)
p_1 = union_split(hash, A[n], eltype_hint, p_1)
n += 1
end
# fold all streams back together
Expand Down