diff --git a/benchmark/single-source/DictTest4.swift b/benchmark/single-source/DictTest4.swift index d2849fae76e47..bc7f38548d572 100644 --- a/benchmark/single-source/DictTest4.swift +++ b/benchmark/single-source/DictTest4.swift @@ -42,6 +42,20 @@ struct LargeKey: Hashable { self.p = value & 8 == 0 self.q = value & 16 == 0 } +#if true // FIXME remove once synthesized hashing generates _hash(into:) + func _hash(into hasher: _UnsafeHasher) -> _UnsafeHasher { + return hasher + .appending(i) + .appending(j) + .appending(k) + .appending(l) + .appending(m) + .appending(n) + .appending(o) + .appending(p) + .appending(q) + } +#endif } @inline(never) diff --git a/include/swift/AST/ASTContext.h b/include/swift/AST/ASTContext.h index ea9125eba6813..cb23062180e0e 100644 --- a/include/swift/AST/ASTContext.h +++ b/include/swift/AST/ASTContext.h @@ -405,8 +405,8 @@ class ASTContext { ProtocolDecl *getErrorDecl() const; CanType getExceptionType() const; -#define KNOWN_STDLIB_TYPE_DECL(NAME, DECL_CLASS, NUM_GENERIC_PARAMS) \ - /** Retrieve the declaration of Swift.NAME. */ \ +#define KNOWN_STDLIB_TYPE_DECL_WITH_NAME(NAME, IDSTR, DECL_CLASS, NUM_GENERIC_PARAMS) \ + /** Retrieve the declaration of Swift.IDSTR. */ \ DECL_CLASS *get##NAME##Decl() const; #include "swift/AST/KnownStdlibTypes.def" diff --git a/include/swift/AST/KnownIdentifiers.def b/include/swift/AST/KnownIdentifiers.def index 0b7f4d4a36dd9..a554e1a07ce80 100644 --- a/include/swift/AST/KnownIdentifiers.def +++ b/include/swift/AST/KnownIdentifiers.def @@ -57,10 +57,12 @@ IDENTIFIER(forKey) IDENTIFIER(from) IDENTIFIER(fromRaw) IDENTIFIER(hashValue) +IDENTIFIER_(hash) IDENTIFIER(init) IDENTIFIER(initialize) IDENTIFIER(initStorage) IDENTIFIER(initialValue) +IDENTIFIER(into) IDENTIFIER(intValue) IDENTIFIER(Key) IDENTIFIER(KeyedDecodingContainer) diff --git a/include/swift/AST/KnownStdlibTypes.def b/include/swift/AST/KnownStdlibTypes.def index d450b13479537..45a870695defa 100644 --- a/include/swift/AST/KnownStdlibTypes.def +++ b/include/swift/AST/KnownStdlibTypes.def @@ -15,16 +15,19 @@ // //===----------------------------------------------------------------------===// -#ifndef KNOWN_STDLIB_TYPE_DECL -/// KNOWN_STDLIB_TYPE_DECL(NAME, DECL_CLASS, NUM_GENERIC_PARAMS) +#ifndef KNOWN_STDLIB_TYPE_DECL_WITH_NAME +/// KNOWN_STDLIB_TYPE_DECL_WITH_NAME(NAME, IDSTR, DECL_CLASS, NUM_GENERIC_PARAMS) /// /// The macro is expanded for each known standard library type. NAME is /// bound to the unqualified name of the type. DECL_CLASS is bound to the /// Decl subclass it is expected to be an instance of. NUM_GENERIC_PARAMS is /// bound to the number of generic parameters the type is expected to have. -#define KNOWN_STDLIB_TYPE_DECL(NAME, DECL_CLASS, NUM_GENERIC_PARAMS) +#define KNOWN_STDLIB_TYPE_DECL_WITH_NAME(NAME, IDSTR, DECL_CLASS, NUM_GENERIC_PARAMS) #endif +#define KNOWN_STDLIB_TYPE_DECL(NAME, DECL_CLASS, NUM_GENERIC_PARAMS) \ + KNOWN_STDLIB_TYPE_DECL_WITH_NAME(NAME, #NAME, DECL_CLASS, NUM_GENERIC_PARAMS) + KNOWN_STDLIB_TYPE_DECL(Bool, NominalTypeDecl, 0) KNOWN_STDLIB_TYPE_DECL(Int, NominalTypeDecl, 0) @@ -50,6 +53,8 @@ KNOWN_STDLIB_TYPE_DECL(Sequence, NominalTypeDecl, 1) KNOWN_STDLIB_TYPE_DECL(Dictionary, NominalTypeDecl, 2) KNOWN_STDLIB_TYPE_DECL(AnyHashable, NominalTypeDecl, 0) KNOWN_STDLIB_TYPE_DECL(MutableCollection, ProtocolDecl, 1) +KNOWN_STDLIB_TYPE_DECL_WITH_NAME(DefaultHasher, "_DefaultHasher", NominalTypeDecl, 0) +KNOWN_STDLIB_TYPE_DECL_WITH_NAME(Hasher, "_Hasher", ProtocolDecl, 1) KNOWN_STDLIB_TYPE_DECL(AnyKeyPath, NominalTypeDecl, 0) KNOWN_STDLIB_TYPE_DECL(PartialKeyPath, NominalTypeDecl, 1) @@ -79,3 +84,4 @@ KNOWN_STDLIB_TYPE_DECL(KeyedDecodingContainer, NominalTypeDecl, 1) KNOWN_STDLIB_TYPE_DECL(RangeReplaceableCollection, ProtocolDecl, 1) #undef KNOWN_STDLIB_TYPE_DECL +#undef KNOWN_STDLIB_TYPE_DECL_WITH_NAME diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index 82b79b6f48ab6..e2be5e22d83ad 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -135,7 +135,7 @@ struct ASTContext::Implementation { /// The AnyObject type. CanType AnyObjectType; -#define KNOWN_STDLIB_TYPE_DECL(NAME, DECL_CLASS, NUM_GENERIC_PARAMS) \ +#define KNOWN_STDLIB_TYPE_DECL_WITH_NAME(NAME, IDSTR, DECL_CLASS, NUM_GENERIC_PARAMS) \ /** The declaration of Swift.NAME. */ \ DECL_CLASS *NAME##Decl = nullptr; #include "swift/AST/KnownStdlibTypes.def" @@ -622,11 +622,11 @@ FuncDecl *ASTContext::getPlusFunctionOnString() const { return Impl.PlusFunctionOnString; } -#define KNOWN_STDLIB_TYPE_DECL(NAME, DECL_CLASS, NUM_GENERIC_PARAMS) \ +#define KNOWN_STDLIB_TYPE_DECL_WITH_NAME(NAME, IDSTR, DECL_CLASS, NUM_GENERIC_PARAMS) \ DECL_CLASS *ASTContext::get##NAME##Decl() const { \ if (!Impl.NAME##Decl) \ Impl.NAME##Decl = dyn_cast_or_null( \ - findStdlibType(*this, #NAME, NUM_GENERIC_PARAMS)); \ + findStdlibType(*this, IDSTR, NUM_GENERIC_PARAMS)); \ return Impl.NAME##Decl; \ } #include "swift/AST/KnownStdlibTypes.def" diff --git a/stdlib/public/core/AnyHashable.swift b/stdlib/public/core/AnyHashable.swift index ef3087b7c95eb..79568acdc2ac7 100644 --- a/stdlib/public/core/AnyHashable.swift +++ b/stdlib/public/core/AnyHashable.swift @@ -48,6 +48,7 @@ internal protocol _AnyHashableBox { /// no comparison is possible. Otherwise, contains the result of `==`. func _isEqual(to: _AnyHashableBox) -> Bool? var _hashValue: Int { get } + func _hash(_into hasher: _UnsafeHasher) -> _UnsafeHasher var _base: Any { get } func _downCastConditional(into result: UnsafeMutablePointer) -> Bool @@ -93,6 +94,12 @@ internal struct _ConcreteHashableBox : _AnyHashableBox { return _baseHashable.hashValue } + @_inlineable // FIXME(sil-serialize-all) + @_versioned // FIXME(sil-serialize-all) + func _hash(_into hasher: _UnsafeHasher) -> _UnsafeHasher { + return _baseHashable._hash(into: hasher) + } + @_inlineable // FIXME(sil-serialize-all) @_versioned // FIXME(sil-serialize-all) internal var _base: Any { @@ -295,6 +302,11 @@ extension AnyHashable : Hashable { public var hashValue: Int { return _box._hashValue } + + @_inlineable // FIXME(sil-serialize-all) + public func _hash(into hasher: _UnsafeHasher) -> _UnsafeHasher { + return _box._hash(_into: hasher) + } } extension AnyHashable : CustomStringConvertible { diff --git a/stdlib/public/core/Bool.swift b/stdlib/public/core/Bool.swift index 6ab1c1780cb5b..80ca877f8980f 100644 --- a/stdlib/public/core/Bool.swift +++ b/stdlib/public/core/Bool.swift @@ -159,6 +159,12 @@ extension Bool : Equatable, Hashable { return self ? 1 : 0 } + @_inlineable // FIXME(sil-serialize-all) + @_transparent + public func _hash(into hasher: _UnsafeHasher) -> _UnsafeHasher { + return hasher.appending(self ? 1 : 0) + } + @_inlineable // FIXME(sil-serialize-all) @_transparent public static func == (lhs: Bool, rhs: Bool) -> Bool { diff --git a/stdlib/public/core/CTypes.swift b/stdlib/public/core/CTypes.swift index bf73e273a3601..fcda8bf1f62de 100644 --- a/stdlib/public/core/CTypes.swift +++ b/stdlib/public/core/CTypes.swift @@ -166,6 +166,11 @@ extension OpaquePointer: Hashable { public var hashValue: Int { return Int(Builtin.ptrtoint_Word(_rawValue)) } + + @_inlineable // FIXME(sil-serialize-all) + public func _hash(into hasher: _UnsafeHasher) -> _UnsafeHasher { + return hasher.appending(Int(bitPattern: self)) + } } extension OpaquePointer : CustomDebugStringConvertible { diff --git a/stdlib/public/core/DoubleWidth.swift.gyb b/stdlib/public/core/DoubleWidth.swift.gyb index b7474bbc85635..1ef5dddc85e30 100644 --- a/stdlib/public/core/DoubleWidth.swift.gyb +++ b/stdlib/public/core/DoubleWidth.swift.gyb @@ -151,10 +151,12 @@ extension DoubleWidth : Comparable { extension DoubleWidth : Hashable { @_inlineable // FIXME(sil-serialize-all) public var hashValue: Int { - var result = 0 - result = _combineHashValues(result, _storage.high.hashValue) - result = _combineHashValues(result, _storage.low.hashValue) - return result + return _defaultHashValue(for: self) + } + + @_inlineable // FIXME(sil-serialize-all) + public func _hash(into hasher: _UnsafeHasher) -> _UnsafeHasher { + return hasher.appending(low).appending(high) } } diff --git a/stdlib/public/core/FloatingPointTypes.swift.gyb b/stdlib/public/core/FloatingPointTypes.swift.gyb index 36bd1c67cb094..2b96e7fad67ee 100644 --- a/stdlib/public/core/FloatingPointTypes.swift.gyb +++ b/stdlib/public/core/FloatingPointTypes.swift.gyb @@ -1440,26 +1440,26 @@ extension ${Self} : Hashable { /// your program. Do not save hash values to use during a future execution. @_inlineable // FIXME(sil-serialize-all) public var hashValue: Int { + return _defaultHashValue(for: self) + } + + @_inlineable // FIXME(sil-serialize-all) + public func _hash(into hasher: _UnsafeHasher) -> _UnsafeHasher { + var v = self if isZero { // To satisfy the axiom that equality implies hash equality, we need to // finesse the hash value of -0.0 to match +0.0. - return 0 - } else { - %if bits <= word_bits: - return Int(bitPattern: UInt(bitPattern)) - %elif bits == 64: # Double -> 32-bit Int - return Int(truncatingIfNeeded: bitPattern &>> 32) ^ - Int(truncatingIfNeeded: bitPattern) - %elif word_bits == 32: # Float80 -> 32-bit Int - return Int(truncatingIfNeeded: significandBitPattern &>> 32) ^ - Int(truncatingIfNeeded: significandBitPattern) ^ - Int(_representation.signAndExponent) - %else: # Float80 -> 64-bit Int - return Int(bitPattern: UInt(significandBitPattern)) ^ - Int(_representation.signAndExponent) - %end + v = 0 } + %if bits == 80: + return hasher + .appending(v._representation.signAndExponent) + .appending(v.significandBitPattern) + %else: + return hasher.appending(v.bitPattern) + %end } + } extension ${Self} { diff --git a/stdlib/public/core/Hashable.swift b/stdlib/public/core/Hashable.swift index 98fd5ffb70390..5b91e8e43257d 100644 --- a/stdlib/public/core/Hashable.swift +++ b/stdlib/public/core/Hashable.swift @@ -108,6 +108,26 @@ public protocol Hashable : Equatable { /// Hash values are not guaranteed to be equal across different executions of /// your program. Do not save hash values to use during a future execution. var hashValue: Int { get } + + func _hash(into hasher: _UnsafeHasher) -> _UnsafeHasher +} + +@_versioned +@_inlineable +@inline(__always) +internal func _defaultHashValue(for value: T) -> Int { + var hasher = _Hasher(_inlineable: ()) + return withUnsafeMutablePointer(to: &hasher) { p in + return _UnsafeHasher(p).appending(value).finalized() + } +} + +extension Hashable { + @_inlineable + @inline(__always) + public func _hash(into hasher: _UnsafeHasher) -> _UnsafeHasher { + return hasher.appending(self.hashValue) + } } // Called by the SwiftValue implementation. @@ -127,3 +147,95 @@ internal func Hashable_hashValue_indirect( return value.pointee.hashValue } +/// An unsafe wrapper around a stateful hash function, presenting a faux purely +/// functional interface to eliminate ARC overhead. +/// +/// This is not a true value type; calling `appending` or `finalized` actually +/// mutates `self`'s state. +@_fixed_layout +public struct _UnsafeHasher { + @_versioned + internal let _state: UnsafeMutablePointer<_Hasher> + + @_inlineable + @_versioned + internal init(_ state: UnsafeMutablePointer<_Hasher>) { + self._state = state + } + + @effects(readonly) + @inline(never) + public func appending(_ value: Int) -> _UnsafeHasher { + // The effects attribute is a lie; however, it enables the compiler to + // eliminate unnecessary retain/releases protecting Hashable state around + // calls to `_Hasher.append(_:)`. + // + // We don't have a way to describe the side-effects of an opaque function -- + // if it doesn't have an @effects attribute, the compiler has no choice but + // to assume it may mutate the hashable we're visiting. We know that won't + // be the case (the stdlib owns the hash function), but the only way to tell + // this to the compiler is to pretend the state update is pure. + _state.pointee._append_alwaysInline(value) + return self + } + + @_inlineable + @_transparent + public func appending(_ value: H) -> _UnsafeHasher { + return value._hash(into: self) + } + + @_inlineable + @_versioned + internal func finalized() -> Int { + return _state.pointee.finalize() + } +} + +// FIXME: This is purely for benchmarking; to be removed. +@_fixed_layout +public struct _QuickHasher { + @_versioned + internal var _hash: Int + + @inline(never) + public init() { + _hash = 0 + } + + @_inlineable + @_versioned + internal init(_inlineable: Void) { + _hash = 0 + } + + @inline(never) + public mutating func append(_ value: Int) { + _append_alwaysInline(value) + } + + @_inlineable + @_versioned + @inline(__always) + internal mutating func _append_alwaysInline(_ value: Int) { + if _hash == 0 { + _hash = value + return + } + _hash = _combineHashValues(_hash, value) + } + + @inline(never) + public mutating func finalize() -> Int { + return _finalize_alwaysInline() + } + + @_inlineable // FIXME(sil-serialize-all) + @_versioned + @inline(__always) + internal mutating func _finalize_alwaysInline() -> Int { + return _mixInt(_hash) + } +} + +public typealias _Hasher = _SipHash13 diff --git a/stdlib/public/core/HashedCollections.swift.gyb b/stdlib/public/core/HashedCollections.swift.gyb index 8045b6a43d565..5181c52cf8228 100644 --- a/stdlib/public/core/HashedCollections.swift.gyb +++ b/stdlib/public/core/HashedCollections.swift.gyb @@ -792,11 +792,16 @@ extension Set : Hashable { @_inlineable // FIXME(sil-serialize-all) public var hashValue: Int { // FIXME(ABI)#177: Cache Set hashValue - var result: Int = _mixInt(0) + return _defaultHashValue(for: self) + } + + @_inlineable // FIXME(sil-serialize-all) + public func _hash(into hasher: _UnsafeHasher) -> _UnsafeHasher { + var hash = 0 for member in self { - result ^= _mixInt(member.hashValue) + hash ^= _defaultHashValue(for: member) } - return result + return hasher.appending(hash) } } @@ -3984,7 +3989,7 @@ extension _Native${Self}Buffer @_versioned @inline(__always) // For performance reasons. internal func _bucket(_ k: Key) -> Int { - return _squeezeHashValue(k.hashValue, bucketCount) + return _defaultHashValue(for: k) & _bucketMask } @_inlineable // FIXME(sil-serialize-all) diff --git a/stdlib/public/core/Hashing.swift b/stdlib/public/core/Hashing.swift index 02acd8e2b5551..50669623305c6 100644 --- a/stdlib/public/core/Hashing.swift +++ b/stdlib/public/core/Hashing.swift @@ -159,35 +159,6 @@ func _mixInt(_ value: Int) -> Int { #endif } -/// Given a hash value, returns an integer value in the range of -/// 0..<`upperBound` that corresponds to a hash value. -/// -/// The `upperBound` must be positive and a power of 2. -/// -/// This function is superior to computing the remainder of `hashValue` by -/// the range length. Some types have bad hash functions; sometimes simple -/// patterns in data sets create patterns in hash values and applying the -/// remainder operation just throws away even more information and invites -/// even more hash collisions. This effect is especially bad because the -/// range is a power of two, which means to throws away high bits of the hash -/// (which would not be a problem if the hash was known to be good). This -/// function mixes the bits in the hash value to compensate for such cases. -/// -/// Of course, this function is a compressing function, and applying it to a -/// hash value does not change anything fundamentally: collisions are still -/// possible, and it does not prevent malicious users from constructing data -/// sets that will exhibit pathological collisions. -@_inlineable // FIXME(sil-serialize-all) -public // @testable -func _squeezeHashValue(_ hashValue: Int, _ upperBound: Int) -> Int { - _sanityCheck(_isPowerOf2(upperBound)) - let mixedHashValue = _mixInt(hashValue) - - // As `upperBound` is a power of two we can do a bitwise-and to calculate - // mixedHashValue % upperBound. - return mixedHashValue & (upperBound &- 1) -} - /// Returns a new value that combines the two given hash values. /// /// Combining is performed using [a hash function][ref] described by T.C. Hoad diff --git a/stdlib/public/core/Integers.swift.gyb b/stdlib/public/core/Integers.swift.gyb index 04a61d805bdc3..45a121fced88f 100644 --- a/stdlib/public/core/Integers.swift.gyb +++ b/stdlib/public/core/Integers.swift.gyb @@ -3603,22 +3603,20 @@ extension ${Self} : Hashable { public var hashValue: Int { @inline(__always) get { -% if bits <= word_bits and signed: - // Sign extend the value. - return Int(self) -% elif bits <= word_bits and not signed: - // Sign extend the value. - return Int(${OtherSelf}(bitPattern: self)) -% elif bits == word_bits * 2: - // We have twice as many bits as we need to return. - return - Int(truncatingIfNeeded: self) ^ - Int(truncatingIfNeeded: self &>> 32) -% else: - _Unimplemented() -% end + return _defaultHashValue(for: self) } } + + @_inlineable // FIXME(sil-serialize-all) + public func _hash(into hasher: _UnsafeHasher) -> _UnsafeHasher { + % if bits <= word_bits: + return hasher.appending(Int(bitPattern: _lowWord)) + % else: + var hasher = hasher + words.forEach { hasher = hasher.appending(Int(bitPattern: $0)) } + return hasher + % end + } } diff --git a/stdlib/public/core/KeyPath.swift b/stdlib/public/core/KeyPath.swift index 70dace66da613..93f1790f1ae61 100644 --- a/stdlib/public/core/KeyPath.swift +++ b/stdlib/public/core/KeyPath.swift @@ -49,21 +49,27 @@ public class AnyKeyPath: Hashable, _AppendKeyPath { @_inlineable // FIXME(sil-serialize-all) final public var hashValue: Int { - var hash = 0 - withBuffer { + return _defaultHashValue(for: self) + } + + @_inlineable // FIXME(sil-serialize-all) + public func _hash(into hasher: _UnsafeHasher) -> _UnsafeHasher { + return withBuffer { + var hasher = hasher var buffer = $0 while true { let (component, type) = buffer.next() - hash ^= _mixInt(component.value.hashValue) + hasher = hasher.appending(component.value) if let type = type { - hash ^= _mixInt(unsafeBitCast(type, to: Int.self)) + hasher = hasher.appending(unsafeBitCast(type, to: Int.self)) } else { break } } + return hasher } - return hash } + @_inlineable // FIXME(sil-serialize-all) public static func ==(a: AnyKeyPath, b: AnyKeyPath) -> Bool { // Fast-path identical objects @@ -452,12 +458,17 @@ internal struct ComputedPropertyID: Hashable { @_inlineable // FIXME(sil-serialize-all) @_versioned // FIXME(sil-serialize-all) internal var hashValue: Int { - var hash = 0 - hash ^= _mixInt(value) - hash ^= _mixInt(isStoredProperty ? 13 : 17) - hash ^= _mixInt(isTableOffset ? 19 : 23) - return hash + return _defaultHashValue(for: self) } + + @_inlineable // FIXME(sil-serialize-all) + public func _hash(into hasher: _UnsafeHasher) -> _UnsafeHasher { + return hasher + .appending(value) + .appending(isStoredProperty) + .appending(isTableOffset) + } + } @_versioned // FIXME(sil-serialize-all) diff --git a/stdlib/public/core/SipHash.swift.gyb b/stdlib/public/core/SipHash.swift.gyb index 558cca3bef75b..217ba7edf7717 100644 --- a/stdlib/public/core/SipHash.swift.gyb +++ b/stdlib/public/core/SipHash.swift.gyb @@ -29,52 +29,6 @@ internal enum _SipHashDetail { return (x &<< UInt64(amount)) | (x &>> UInt64(64 - amount)) } - @_inlineable // FIXME(sil-serialize-all) - @_versioned - @inline(__always) - internal static func _loadUnalignedUInt64LE( - from p: UnsafeRawPointer - ) -> UInt64 { - // FIXME(integers): split into multiple expressions to speed up the - // typechecking - var result = - UInt64(p.load(fromByteOffset: 0, as: UInt8.self)) - result |= - (UInt64(p.load(fromByteOffset: 1, as: UInt8.self)) &<< (8 as UInt64)) - result |= - (UInt64(p.load(fromByteOffset: 2, as: UInt8.self)) &<< (16 as UInt64)) - result |= - (UInt64(p.load(fromByteOffset: 3, as: UInt8.self)) &<< (24 as UInt64)) - result |= - (UInt64(p.load(fromByteOffset: 4, as: UInt8.self)) &<< (32 as UInt64)) - result |= - (UInt64(p.load(fromByteOffset: 5, as: UInt8.self)) &<< (40 as UInt64)) - result |= - (UInt64(p.load(fromByteOffset: 6, as: UInt8.self)) &<< (48 as UInt64)) - result |= - (UInt64(p.load(fromByteOffset: 7, as: UInt8.self)) &<< (56 as UInt64)) - return result - } - - @_inlineable // FIXME(sil-serialize-all) - @_versioned - @inline(__always) - internal static func _loadPartialUnalignedUInt64LE( - from p: UnsafeRawPointer, - byteCount: Int - ) -> UInt64 { - _sanityCheck((0..<8).contains(byteCount)) - var result: UInt64 = 0 - if byteCount >= 1 { result |= UInt64(p.load(fromByteOffset: 0, as: UInt8.self)) } - if byteCount >= 2 { result |= UInt64(p.load(fromByteOffset: 1, as: UInt8.self)) &<< (8 as UInt64) } - if byteCount >= 3 { result |= UInt64(p.load(fromByteOffset: 2, as: UInt8.self)) &<< (16 as UInt64) } - if byteCount >= 4 { result |= UInt64(p.load(fromByteOffset: 3, as: UInt8.self)) &<< (24 as UInt64) } - if byteCount >= 5 { result |= UInt64(p.load(fromByteOffset: 4, as: UInt8.self)) &<< (32 as UInt64) } - if byteCount >= 6 { result |= UInt64(p.load(fromByteOffset: 5, as: UInt8.self)) &<< (40 as UInt64) } - if byteCount >= 7 { result |= UInt64(p.load(fromByteOffset: 6, as: UInt8.self)) &<< (48 as UInt64) } - return result - } - @_inlineable // FIXME(sil-serialize-all) @_versioned @inline(__always) @@ -102,7 +56,7 @@ internal enum _SipHashDetail { } % for (c_rounds, d_rounds) in [(2, 4), (1, 3)]: -% Self = '_SipHash{}{}Context'.format(c_rounds, d_rounds) +% Self = '_SipHash{}{}'.format(c_rounds, d_rounds) @_fixed_layout // FIXME(sil-serialize-all) public // @testable @@ -123,15 +77,7 @@ struct ${Self} { @_versioned internal var hashedByteCount: UInt64 = 0 - @_versioned - internal var dataTail: UInt64 = 0 - - @_versioned - internal var dataTailByteCount: Int = 0 - - @_versioned - internal var finalizedHash: UInt64? - +// @inline(never) @_inlineable // FIXME(sil-serialize-all) public init(key: (UInt64, UInt64)) { v3 ^= key.1 @@ -140,72 +86,38 @@ struct ${Self} { v0 ^= key.0 } - // FIXME(ABI)#62 (UnsafeRawBufferPointer): Use UnsafeRawBufferPointer. - @_inlineable // FIXME(sil-serialize-all) - public // @testable - mutating func append(_ data: UnsafeRawPointer, byteCount: Int) { - _append_alwaysInline(data, byteCount: byteCount) + @inline(never) + public init() { + self.init(key: _Hashing.secretKey) } - // FIXME(ABI)#63 (UnsafeRawBufferPointer): Use UnsafeRawBufferPointer. - @_inlineable // FIXME(sil-serialize-all) + @_inlineable @_versioned - @inline(__always) - internal mutating func _append_alwaysInline( - _ data: UnsafeRawPointer, - byteCount: Int - ) { - precondition(finalizedHash == nil) - _sanityCheck((0..<8).contains(dataTailByteCount)) - - let dataEnd = data + byteCount - - var data = data - var byteCount = byteCount - if dataTailByteCount != 0 { - let restByteCount = min( - MemoryLayout.size - dataTailByteCount, - byteCount) - let rest = _SipHashDetail._loadPartialUnalignedUInt64LE( - from: data, - byteCount: restByteCount) - dataTail |= rest &<< UInt64(dataTailByteCount * 8) - dataTailByteCount += restByteCount - data += restByteCount - byteCount -= restByteCount - } + internal init(_inlineable: Void) { + self.init(key: _Hashing.secretKey) + } - if dataTailByteCount == MemoryLayout.size { - _appendDirectly(dataTail) - dataTail = 0 - dataTailByteCount = 0 - } else if dataTailByteCount != 0 { - _sanityCheck(data == dataEnd) - return - } + @inline(never) + public mutating func append(_ m: Int) { + _append_alwaysInline(m) + } - let endOfWords = - data + byteCount - (byteCount % MemoryLayout.size) - while data != endOfWords { - _appendDirectly(_SipHashDetail._loadUnalignedUInt64LE(from: data)) - data += 8 - // No need to update `byteCount`, it is not used beyond this point. - } + @inline(never) + public mutating func append(_ m: UInt64) { + _append_alwaysInline(m) + } - if data != dataEnd { - dataTailByteCount = dataEnd - data - dataTail = _SipHashDetail._loadPartialUnalignedUInt64LE( - from: data, - byteCount: dataTailByteCount) - } + @_inlineable + @_versioned + @inline(__always) + internal mutating func _append_alwaysInline(_ m: Int) { + _append_alwaysInline(UInt64(truncatingIfNeeded: UInt(bitPattern: m))) } - /// This function mixes in the given word directly into the state, - /// ignoring `dataTail`. - @_inlineable // FIXME(sil-serialize-all) + @_inlineable @_versioned @inline(__always) - internal mutating func _appendDirectly(_ m: UInt64) { + internal mutating func _append_alwaysInline(_ m: UInt64) { v3 ^= m for _ in 0..<${c_rounds} { _SipHashDetail._sipRound(v0: &v0, v1: &v1, v2: &v2, v3: &v3) @@ -214,33 +126,38 @@ struct ${Self} { hashedByteCount += 8 } -% for data_type in ['UInt', 'Int', 'UInt64', 'Int64', 'UInt32', 'Int32']: - @_inlineable // FIXME(sil-serialize-all) + @inline(never) public // @testable - mutating func append(_ data: ${data_type}) { - var data = data - _append_alwaysInline(&data, byteCount: MemoryLayout.size(ofValue: data)) + mutating func finalize() -> Int { + return _finalize_alwaysInline() } -% end - @_inlineable // FIXME(sil-serialize-all) + @inline(never) + @_inlineable public // @testable - mutating func finalizeAndReturnHash() -> UInt64 { - return _finalizeAndReturnHash_alwaysInline() + mutating func finalize(tail: UInt64, tailByteCount: Int) -> UInt64 { + return _finalize_alwaysInline(tail: tail, tailByteCount: tailByteCount) } @_inlineable // FIXME(sil-serialize-all) @_versioned @inline(__always) - internal mutating func _finalizeAndReturnHash_alwaysInline() -> UInt64 { - if let finalizedHash = finalizedHash { - return finalizedHash - } + internal mutating func _finalize_alwaysInline() -> Int { + return Int( + truncatingIfNeeded: _finalize_alwaysInline(tail: 0, tailByteCount: 0)) + } - _sanityCheck((0..<8).contains(dataTailByteCount)) + @_inlineable // FIXME(sil-serialize-all) + @_versioned + @inline(__always) + internal mutating func _finalize_alwaysInline( + tail: UInt64, + tailByteCount: Int + ) -> UInt64 { + _sanityCheck((0..<8).contains(tailByteCount)) - hashedByteCount += UInt64(dataTailByteCount) - let b: UInt64 = (hashedByteCount << 56) | dataTail + hashedByteCount += UInt64(tailByteCount) + let b: UInt64 = (hashedByteCount << 56) | tail v3 ^= b for _ in 0..<${c_rounds} { @@ -254,47 +171,7 @@ struct ${Self} { _SipHashDetail._sipRound(v0: &v0, v1: &v1, v2: &v2, v3: &v3) } - finalizedHash = v0 ^ v1 ^ v2 ^ v3 - return finalizedHash! - } - - @_inlineable // FIXME(sil-serialize-all) - @_versioned // FIXME(sil-serialize-all) - internal mutating func _finalizeAndReturnIntHash() -> Int { - let hash: UInt64 = finalizeAndReturnHash() -#if arch(i386) || arch(arm) - return Int(truncatingIfNeeded: hash) -#elseif arch(x86_64) || arch(arm64) || arch(powerpc64) || arch(powerpc64le) || arch(s390x) - return Int(Int64(bitPattern: hash)) -#endif - } - - // FIXME(ABI)#64 (UnsafeRawBufferPointer): Use UnsafeRawBufferPointer. - @_inlineable // FIXME(sil-serialize-all) - public // @testable - static func hash( - data: UnsafeRawPointer, - dataByteCount: Int, - key: (UInt64, UInt64) - ) -> UInt64 { - return ${Self}._hash_alwaysInline( - data: data, - dataByteCount: dataByteCount, - key: key) - } - - // FIXME(ABI)#65 (UnsafeRawBufferPointer): Use UnsafeRawBufferPointer. - @_inlineable // FIXME(sil-serialize-all) - @inline(__always) - public // @testable - static func _hash_alwaysInline( - data: UnsafeRawPointer, - dataByteCount: Int, - key: (UInt64, UInt64) - ) -> UInt64 { - var context = ${Self}(key: key) - context._append_alwaysInline(data, byteCount: dataByteCount) - return context._finalizeAndReturnHash_alwaysInline() + return v0 ^ v1 ^ v2 ^ v3 } } % end diff --git a/stdlib/public/core/StringHashable.swift b/stdlib/public/core/StringHashable.swift index 7607c9bba783a..5bd0989987972 100644 --- a/stdlib/public/core/StringHashable.swift +++ b/stdlib/public/core/StringHashable.swift @@ -40,17 +40,17 @@ extension Unicode { _ string: UnsafeBufferPointer ) -> Int { let collationTable = _swift_stdlib_unicode_getASCIICollationTable() - var hasher = _SipHash13Context(key: _Hashing.secretKey) + var hasher = _Hasher() for c in string { _precondition(c <= 127) let element = collationTable[Int(c)] // Ignore zero valued collation elements. They don't participate in the // ordering relation. if element != 0 { - hasher.append(element) + hasher.append(Int(truncatingIfNeeded: element)) } } - return hasher._finalizeAndReturnIntHash() + return hasher.finalize() } // FIXME: cannot be marked @_versioned. See @@ -64,7 +64,7 @@ extension Unicode { UInt32(string.count)) defer { _swift_stdlib_unicodeCollationIterator_delete(collationIterator) } - var hasher = _SipHash13Context(key: _Hashing.secretKey) + var hasher = _Hasher() while true { var hitEnd = false let element = @@ -75,10 +75,10 @@ extension Unicode { // Ignore zero valued collation elements. They don't participate in the // ordering relation. if element != 0 { - hasher.append(element) + hasher.append(Int(truncatingIfNeeded: element)) } } - return hasher._finalizeAndReturnIntHash() + return hasher.finalize() } } diff --git a/stdlib/public/core/UnsafePointer.swift.gyb b/stdlib/public/core/UnsafePointer.swift.gyb index e6ff2a114e67a..3824dc742c4ae 100644 --- a/stdlib/public/core/UnsafePointer.swift.gyb +++ b/stdlib/public/core/UnsafePointer.swift.gyb @@ -904,8 +904,13 @@ extension ${Self}: Hashable { public var hashValue: Int { return Int(bitPattern: self) } + + @_inlineable // FIXME(sil-serialize-all) + public func _hash(into hasher: _UnsafeHasher) -> _UnsafeHasher { + return hasher.appending(Int(bitPattern: self)) + } } - + extension ${Self}: Strideable { /// Returns a pointer to the next consecutive instance. ///