Skip to content
Closed
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
14 changes: 14 additions & 0 deletions benchmark/single-source/DictTest4.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
4 changes: 2 additions & 2 deletions include/swift/AST/ASTContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down
2 changes: 2 additions & 0 deletions include/swift/AST/KnownIdentifiers.def
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
12 changes: 9 additions & 3 deletions include/swift/AST/KnownStdlibTypes.def
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)
Expand Down Expand Up @@ -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
6 changes: 3 additions & 3 deletions lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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<DECL_CLASS>( \
findStdlibType(*this, #NAME, NUM_GENERIC_PARAMS)); \
findStdlibType(*this, IDSTR, NUM_GENERIC_PARAMS)); \
return Impl.NAME##Decl; \
}
#include "swift/AST/KnownStdlibTypes.def"
Expand Down
12 changes: 12 additions & 0 deletions stdlib/public/core/AnyHashable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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<T>(into result: UnsafeMutablePointer<T>) -> Bool
Expand Down Expand Up @@ -93,6 +94,12 @@ internal struct _ConcreteHashableBox<Base : Hashable> : _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 {
Expand Down Expand Up @@ -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 {
Expand Down
6 changes: 6 additions & 0 deletions stdlib/public/core/Bool.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
5 changes: 5 additions & 0 deletions stdlib/public/core/CTypes.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
10 changes: 6 additions & 4 deletions stdlib/public/core/DoubleWidth.swift.gyb
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}

Expand Down
30 changes: 15 additions & 15 deletions stdlib/public/core/FloatingPointTypes.swift.gyb
Original file line number Diff line number Diff line change
Expand Up @@ -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} {
Expand Down
112 changes: 112 additions & 0 deletions stdlib/public/core/Hashable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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<T : Hashable>(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.
Expand All @@ -127,3 +147,95 @@ internal func Hashable_hashValue_indirect<T : Hashable>(
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<H: Hashable>(_ 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
13 changes: 9 additions & 4 deletions stdlib/public/core/HashedCollections.swift.gyb
Original file line number Diff line number Diff line change
Expand Up @@ -792,11 +792,16 @@ extension Set : Hashable {
@_inlineable // FIXME(sil-serialize-all)
public var hashValue: Int {
// FIXME(ABI)#177: <rdar://problem/18915294> Cache Set<T> 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)
}
}

Expand Down Expand Up @@ -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)
Expand Down
Loading