Skip to content

Commit 4ff28f2

Browse files
committed
Implement Tuple Hashable Conformance
1 parent 58e643e commit 4ff28f2

File tree

8 files changed

+375
-9
lines changed

8 files changed

+375
-9
lines changed

include/swift/Runtime/BuiltinProtocolConformances.h

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,45 @@ bool _swift_tupleComparable_greaterThan(OpaqueValue *tuple1, OpaqueValue *tuple2
112112
SWIFT_CONTEXT Metadata *swiftSelf,
113113
Metadata *Self, void *witnessTable);
114114

115+
//===----------------------------------------------------------------------===//
116+
// Tuple Hashable Conformance
117+
//===----------------------------------------------------------------------===//
118+
119+
// public protocol Hashable {}
120+
#define SWIFT_HASHABLE_MANGLING SH
121+
122+
#define HASHABLE_DESCRIPTOR PROTOCOL_DESCRIPTOR_SYM(SWIFT_HASHABLE_MANGLING)
123+
124+
#define HASHABLE_DESCRIPTOR_SYMBOL SYMBOL("$sSHMp")
125+
126+
// Swift._hashValue<A where A: Swift.Hashable>(for: A) -> Swift.Int
127+
#define SWIFT_HASHVALUE_FUNC $ss10_hashValue3forSix_tSHRzlF
128+
// Swift.Hasher.combine<A where A: Swift.Hashable>(A) -> ()
129+
#define SWIFT_HASHER_COMBINE_FUNC $ss6HasherV7combineyyxSHRzlF
130+
131+
#define HASHABLE_BASE_CONFORMANCE_DESCRIPTOR SYMBOL("$sSHSQTb")
132+
#define HASHABLE_HASHVALUE_METHOD_DESCRIPTOR SYMBOL("$sSH9hashValueSivgTq")
133+
#define HASHABLE_HASH_METHOD_DESCRIPTOR SYMBOL("$sSH4hash4intoys6HasherVz_tFTq")
134+
#define HASHABLE_RAWHASHVALUE_METHOD_DESCRIPTOR \
135+
SYMBOL("$sSH13_rawHashValue4seedS2i_tFTq")
136+
137+
#define TUPLE_HASHABLE_CONF SYMBOL("_swift_tupleHashable_conf")
138+
#define TUPLE_HASHABLE_HASHVALUE SYMBOL("_swift_tupleHashable_hashValue")
139+
#define TUPLE_HASHABLE_HASH SYMBOL("_swift_tupleHashable_hash")
140+
141+
/// The protocol witness for Swift.Hashable.hashValue.getter: Swift.Int in
142+
/// conformance (A...): Swift.Hashable in Swift.
143+
SWIFT_RUNTIME_EXPORT SWIFT_CC(swift)
144+
intptr_t _swift_tupleHashable_hashValue(SWIFT_CONTEXT OpaqueValue *tuple,
145+
Metadata *Self, void *witnessTable);
146+
147+
/// The protocol witness for Swift.Hashable.hash(into:) in conformance
148+
/// (A...): Swift.Hashable in Swift.
149+
SWIFT_RUNTIME_EXPORT SWIFT_CC(swift)
150+
void _swift_tupleHashable_hash(OpaqueValue *hasher,
151+
SWIFT_CONTEXT OpaqueValue *tuple,
152+
Metadata *Self, void *witnessTable);
153+
115154
} // end namespace swift
116155

117156
#endif

lib/AST/Module.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1014,12 +1014,13 @@ LookupConformanceInModuleRequest::evaluate(
10141014
return ProtocolConformanceRef(protocol);
10151015

10161016
// Tuples have builtin conformances implemented within the runtime.
1017-
// These conformances so far consist of Equatable and Comparable.
1017+
// These conformances so far consist of Equatable, Comparable, and Hashable.
10181018
if (auto tuple = type->getAs<TupleType>()) {
10191019
auto equatable = ctx.getProtocol(KnownProtocolKind::Equatable);
10201020
auto comparable = ctx.getProtocol(KnownProtocolKind::Comparable);
1021+
auto hashable = ctx.getProtocol(KnownProtocolKind::Hashable);
10211022

1022-
if (protocol != equatable && protocol != comparable)
1023+
if (protocol != equatable && protocol != comparable && protocol != hashable)
10231024
return ProtocolConformanceRef::forInvalid();
10241025

10251026
SmallVector<ProtocolConformanceRef, 4> elementConformances;

lib/IRGen/IRGenMangler.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@ std::string IRGenMangler::mangleProtocolConformanceDescriptor(
157157
if (conformance->getType()->is<TupleType>()) {
158158
auto equatable = ctx.getProtocol(KnownProtocolKind::Equatable);
159159
auto comparable = ctx.getProtocol(KnownProtocolKind::Comparable);
160+
auto hashable = ctx.getProtocol(KnownProtocolKind::Hashable);
160161

161162
if (conformance->getProtocol() == equatable) {
162163
return "_swift_tupleEquatable_conf";
@@ -166,6 +167,10 @@ std::string IRGenMangler::mangleProtocolConformanceDescriptor(
166167
return "_swift_tupleComparable_conf";
167168
}
168169

170+
if (conformance->getProtocol() == hashable) {
171+
return "_swift_tupleHashable_conf";
172+
}
173+
169174
llvm_unreachable("mangling conformance descriptor for unknown tuple \
170175
protocol");
171176
}

stdlib/public/runtime/BuiltinProtocolConformances.cpp

Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -608,3 +608,203 @@ bool swift::_swift_tupleComparable_greaterThan(OpaqueValue *tuple1,
608608
// each other.
609609
return false;
610610
}
611+
612+
//===----------------------------------------------------------------------===//
613+
// Tuple Hashable Conformance
614+
//===----------------------------------------------------------------------===//
615+
616+
#if defined(__ELF__)
617+
// Create a GOT equivalent for the Hashable reference.
618+
__asm(
619+
" .type got." HASHABLE_DESCRIPTOR_SYMBOL ", @object\n"
620+
" .section .data.rel.ro\n"
621+
" .p2align 3\n"
622+
"got." HASHABLE_DESCRIPTOR_SYMBOL ":\n"
623+
" .quad (" HASHABLE_DESCRIPTOR_SYMBOL ")\n"
624+
" .size got." HASHABLE_DESCRIPTOR_SYMBOL ", 8\n"
625+
);
626+
627+
// Create a GOT equivalent for the Hashable base conformance to Equatable.
628+
__asm(
629+
" .type got." HASHABLE_BASE_CONFORMANCE_DESCRIPTOR ", @object\n"
630+
" .p2align 3\n"
631+
"got." HASHABLE_BASE_CONFORMANCE_DESCRIPTOR ":\n"
632+
" .quad (" HASHABLE_BASE_CONFORMANCE_DESCRIPTOR ")\n"
633+
" .size got." HASHABLE_BASE_CONFORMANCE_DESCRIPTOR ", 8\n"
634+
);
635+
636+
// Create a GOT equivalent for the Hashable.hashValue method descriptor.
637+
__asm(
638+
" .type got." HASHABLE_HASHVALUE_METHOD_DESCRIPTOR ", @object\n"
639+
" .p2align 3\n"
640+
"got." HASHABLE_HASHVALUE_METHOD_DESCRIPTOR ":\n"
641+
" .quad (" HASHABLE_HASHVALUE_METHOD_DESCRIPTOR ")\n"
642+
" .size got." HASHABLE_HASHVALUE_METHOD_DESCRIPTOR ", 8\n"
643+
);
644+
645+
// Create a GOT equivalent for the Hashable.hash(into:) method descriptor.
646+
__asm(
647+
" .type got." HASHABLE_HASH_METHOD_DESCRIPTOR ", @object\n"
648+
" .p2align 3\n"
649+
"got." HASHABLE_HASH_METHOD_DESCRIPTOR ":\n"
650+
" .quad (" HASHABLE_HASH_METHOD_DESCRIPTOR ")\n"
651+
" .size got." HASHABLE_HASH_METHOD_DESCRIPTOR ", 8\n"
652+
);
653+
654+
// Create a GOT equivalent for the Hashable._rawHashValue method descriptor.
655+
__asm(
656+
" .type got." HASHABLE_RAWHASHVALUE_METHOD_DESCRIPTOR ", @object\n"
657+
" .p2align 3\n"
658+
"got." HASHABLE_RAWHASHVALUE_METHOD_DESCRIPTOR ":\n"
659+
" .quad (" HASHABLE_RAWHASHVALUE_METHOD_DESCRIPTOR ")\n"
660+
" .size got." HASHABLE_RAWHASHVALUE_METHOD_DESCRIPTOR ", 8\n"
661+
);
662+
#endif
663+
664+
// Define the conformance descriptor for tuple Hashable. We do this in
665+
// assembly to work around relative reference issues.
666+
__asm(
667+
#if defined(__ELF__)
668+
" .type __swift_tupleHashable_private, @object\n"
669+
" .local __swift_tupleHashable_private\n"
670+
" .comm __swift_tupleHashable_private, 128, 16\n"
671+
" .protected " TUPLE_HASHABLE_CONF "\n"
672+
" .type " TUPLE_HASHABLE_CONF ", @object\n"
673+
" .section .rodata\n"
674+
#elif defined(__MACH__)
675+
" .zerofill __DATA, __bss, __swift_tupleHashable_private, 128, 4\n"
676+
" .section __TEXT, __const\n"
677+
#endif
678+
" .globl " TUPLE_HASHABLE_CONF "\n"
679+
" .p2align 2\n"
680+
TUPLE_HASHABLE_CONF ":\n"
681+
#if defined(__ELF__)
682+
// This is an indirectable relative reference to the GOT equivalent for the
683+
// Hashable protocol descriptor, hence why we add 1 to indicate indirect.
684+
" .long (got." HASHABLE_DESCRIPTOR_SYMBOL " - \
685+
(" TUPLE_HASHABLE_CONF ")) + 1\n"
686+
#elif defined(__MACH__)
687+
" .long " HASHABLE_DESCRIPTOR_SYMBOL "@GOTPCREL + 5\n"
688+
#endif
689+
// 769 is the MetadataKind::Tuple
690+
" .long 769\n"
691+
// This indicates that we have no witness table pattern. We use a generic
692+
// witness table for builtin conformances.
693+
" .long 0\n"
694+
// 196640 are the ConformanceFlags with the type reference bit set to
695+
// MetadataKind, the has resilient witness bit, and the generic witness table
696+
// bit.
697+
" .long 196640\n"
698+
// This 4 is the ResilientWitnessesHeader indicating we have 4 resilient
699+
// witnesses.
700+
" .long 4\n"
701+
#if defined(__ELF__)
702+
// This is an indirectable relative reference to the GOT equivalent for the
703+
// Hashable base conformance for Equatable, hence why we add 1 to indicate
704+
// indirect.
705+
" .long ((got." HASHABLE_BASE_CONFORMANCE_DESCRIPTOR " - \
706+
(" TUPLE_HASHABLE_CONF ")) - 20) + 1\n"
707+
#elif defined(__MACH__)
708+
" .long " HASHABLE_BASE_CONFORMANCE_DESCRIPTOR "@GOTPCREL + 5\n"
709+
#endif
710+
// This is a direct relative reference to the associated conformance for
711+
// Equatable defined above in assembly. NOTE: We intentionally use the
712+
// Comparable implementation for this because the implementation is the same
713+
// for both Hashable and Comparable. Both want to grab the Equatable table
714+
// from its elements whose witness table is located in the same place for both
715+
// protocols. NOTE: This is minus 23 because the associated conformance
716+
// structure is 1 aligned.
717+
" .long ((\"" TUPLE_COMPARABLE_ASSOCIATEDCONFORMANCE "\") - \
718+
(" TUPLE_HASHABLE_CONF ")) - 23\n"
719+
#if defined(__ELF__)
720+
// This is an indirectable relative reference to the GOT equivalent for the
721+
// Hashable.hashValue method descriptor, hence why we add 1 to indicate
722+
// indirect.
723+
" .long ((got." HASHABLE_HASHVALUE_METHOD_DESCRIPTOR " - \
724+
(" TUPLE_HASHABLE_CONF ")) - 28) + 1\n"
725+
#elif defined(__MACH__)
726+
" .long " HASHABLE_HASHVALUE_METHOD_DESCRIPTOR "@GOTPCREL + 5\n"
727+
#endif
728+
// This is a direct relative reference to the hashValue witness defined below.
729+
" .long ((" TUPLE_HASHABLE_HASHVALUE ") - (" TUPLE_HASHABLE_CONF ")) - 32\n"
730+
#if defined(__ELF__)
731+
// This is an indirectable relative reference to the GOT equivalent for the
732+
// Hashable.hash(into:) method descriptor, hence why we add 1 to indicate
733+
// indirect.
734+
" .long ((got." HASHABLE_HASH_METHOD_DESCRIPTOR " - \
735+
(" TUPLE_HASHABLE_CONF ")) - 36) + 1\n"
736+
#elif defined(__MACH__)
737+
" .long " HASHABLE_HASH_METHOD_DESCRIPTOR "@GOTPCREL + 5\n"
738+
#endif
739+
// This is a direct relative reference to the hash(into:) witness defined below.
740+
" .long ((" TUPLE_HASHABLE_HASH ") - (" TUPLE_HASHABLE_CONF ")) - 40\n"
741+
#if defined(__ELF__)
742+
// This is an indirectable relative reference to the GOT equivalent for the
743+
// Hashable._rawHashValue method descriptor, hence why we add 1 to indicate
744+
// indirect.
745+
" .long ((got." HASHABLE_RAWHASHVALUE_METHOD_DESCRIPTOR " - \
746+
(" TUPLE_HASHABLE_CONF ")) - 44) + 1\n"
747+
#elif defined(__MACH__)
748+
" .long " HASHABLE_RAWHASHVALUE_METHOD_DESCRIPTOR "@GOTPCREL + 5\n"
749+
#endif
750+
// This 0 indicates that we are requesting the default implementation for the
751+
// _rawHashValue getter.
752+
" .long 0\n"
753+
// The witness table size in words.
754+
" .short 0\n"
755+
// The witness table private size in words & requires instantiation.
756+
" .short 1\n"
757+
// The witness table instantiator function.
758+
" .long 0\n"
759+
// This is a direct relative reference to the private data for the
760+
// conformance.
761+
" .long (__swift_tupleHashable_private - (" TUPLE_HASHABLE_CONF ")) - 60\n"
762+
#if defined(__ELF__)
763+
" .size " TUPLE_HASHABLE_CONF ", 64\n"
764+
#endif
765+
);
766+
767+
// These are all function values that we reinterpret later.
768+
extern void *SWIFT_HASHVALUE_FUNC;
769+
extern void *SWIFT_HASHER_COMBINE_FUNC;
770+
771+
using HashValueFn = SWIFT_CC(swift) intptr_t(OpaqueValue *value, Metadata *Self,
772+
void *witnessTable);
773+
using HasherCombineFn = SWIFT_CC(swift) void(OpaqueValue *value,
774+
const Metadata *Self,
775+
const WitnessTable *witnessTable,
776+
SWIFT_CONTEXT OpaqueValue *hasher);
777+
778+
SWIFT_RUNTIME_EXPORT SWIFT_CC(swift)
779+
intptr_t _swift_tupleHashable_hashValue(SWIFT_CONTEXT OpaqueValue *tuple,
780+
Metadata *Self, void *witnessTable) {
781+
auto _hashValue = reinterpret_cast<HashValueFn *>(&SWIFT_HASHVALUE_FUNC);
782+
return _hashValue(tuple, Self, witnessTable);
783+
}
784+
785+
SWIFT_RUNTIME_EXPORT SWIFT_CC(swift)
786+
void _swift_tupleHashable_hash(OpaqueValue *hasher,
787+
SWIFT_CONTEXT OpaqueValue *tuple,
788+
Metadata *Self, void *witnessTable) {
789+
auto tupleTy = cast<TupleTypeMetadata>(Self);
790+
auto table = reinterpret_cast<void**>(witnessTable);
791+
792+
// Loop through all elements and hash them into the Hasher.
793+
for (size_t i = 0; i != tupleTy->NumElements; i += 1) {
794+
auto elt = tupleTy->getElement(i);
795+
796+
// Get the element conformance from the private data in the witness table.
797+
auto conformance = reinterpret_cast<const WitnessTable *>(table[-1 - i]);
798+
799+
// Get the element value from the tuple.
800+
auto value = reinterpret_cast<OpaqueValue *>(
801+
reinterpret_cast<char *>(tuple) + elt.Offset);
802+
803+
auto hasherCombine =
804+
reinterpret_cast<HasherCombineFn *>(&SWIFT_HASHER_COMBINE_FUNC);
805+
806+
// Call the combine function on the hasher for this element value and we're
807+
// done!
808+
hasherCombine(value, elt.Type, conformance, hasher);
809+
}
810+
}

stdlib/public/runtime/ProtocolConformance.cpp

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -378,16 +378,18 @@ searchInConformanceCache(const Metadata *type,
378378

379379
extern const ProtocolDescriptor EQUATABLE_DESCRIPTOR;
380380
extern const ProtocolDescriptor COMPARABLE_DESCRIPTOR;
381+
extern const ProtocolDescriptor HASHABLE_DESCRIPTOR;
381382

382383
static bool tupleConformsToProtocol(const Metadata *type,
383384
const ProtocolDescriptor *protocol) {
384385
auto tuple = cast<TupleTypeMetadata>(type);
385386

386-
// At the moment, tuples can only conform to Equatable and Comparable, so
387-
// reject all other protocols.
387+
// At the moment, tuples can only conform to Equatable, Comparable and
388+
// Hashable, so reject all other protocols.
388389
auto equatable = &EQUATABLE_DESCRIPTOR;
389390
auto comparable = &COMPARABLE_DESCRIPTOR;
390-
if (protocol != equatable && protocol != comparable)
391+
auto hashable = &HASHABLE_DESCRIPTOR;
392+
if (protocol != equatable && protocol != comparable && protocol != hashable)
391393
return false;
392394

393395
for (size_t i = 0; i != tuple->NumElements; i += 1) {
@@ -401,6 +403,7 @@ static bool tupleConformsToProtocol(const Metadata *type,
401403

402404
extern const ProtocolConformanceDescriptor _swift_tupleEquatable_conf;
403405
extern const ProtocolConformanceDescriptor _swift_tupleComparable_conf;
406+
extern const ProtocolConformanceDescriptor _swift_tupleHashable_conf;
404407

405408
static const ProtocolConformanceDescriptor *getTupleConformanceDescriptor(
406409
const ProtocolDescriptor *protocol) {
@@ -412,6 +415,10 @@ static const ProtocolConformanceDescriptor *getTupleConformanceDescriptor(
412415
return &_swift_tupleComparable_conf;
413416
}
414417

418+
if (protocol == &HASHABLE_DESCRIPTOR) {
419+
return &_swift_tupleHashable_conf;
420+
}
421+
415422
return nullptr;
416423
}
417424

test/IRGen/builtin_conformances.swift

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
// CHECK-LABEL: @_swift_tupleEquatable_conf = external global %swift.protocol_conformance_descriptor
44
// CHECK-LABEL: @_swift_tupleComparable_conf = external global %swift.protocol_conformance_descriptor
5+
// CHECK-LABEL: @_swift_tupleHashable_conf = external global %swift.protocol_conformance_descriptor
56

67
struct Wrapper<T> {
78
let value: T
@@ -60,3 +61,28 @@ public func testTupleComparable() {
6061
// CHECK: {{%.*}} = call swiftcc i1 {{.*}}({{%.*}}.1* noalias nocapture undef, {{%.*}}.1* noalias nocapture undef, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* @"$sytN", i32 0, i32 1), i8** [[TEST_TUPLE_COMPARABLE_WT1]])
6162
let _ = Wrapper(value: ()) < Wrapper(value: ())
6263
}
64+
65+
//===----------------------------------------------------------------------===//
66+
// Tuple Hashable conformance
67+
//===----------------------------------------------------------------------===//
68+
69+
extension Wrapper: Hashable where T: Hashable {}
70+
71+
public func hashValue<T: Hashable>(for instance: T) -> Int {
72+
instance.hashValue
73+
}
74+
75+
public func useHashable<T: Hashable>(_ thing: T) -> Int {
76+
// CHECK: [[USE_HASHABLE_WT:%.*]] = call i8** @swift_getWitnessTable(%swift.protocol_conformance_descriptor* @_swift_tupleHashable_conf, %swift.type* {{%.*}}, i8*** {{%.*}})
77+
// CHECK-NEXT: {{%.*}} = call swiftcc i64 {{.*}}(%swift.opaque* noalias nocapture {{%.*}}, %swift.type* {{%.*}}, i8** [[USE_HASHABLE_WT]])
78+
hashValue(for: (thing, thing))
79+
}
80+
81+
public func testTupleHashable() {
82+
// CHECK: [[TEST_TUPLE_HASHABLE_WT1:%.*]] = call i8** @swift_getWitnessTable(%swift.protocol_conformance_descriptor* @_swift_tupleHashable_conf, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* @"$sytN", i32 0, i32 1), i8*** undef)
83+
// CHECK: {{%.*}} = call swiftcc i64 {{.*}}(%swift.opaque* noalias nocapture undef, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* @"$sytN", i32 0, i32 1), i8** [[TEST_TUPLE_HASHABLE_WT1]])
84+
let _ = hashValue(for: ())
85+
86+
// CHECK: {{%.*}} = call swiftcc i64 {{.*}}(%swift.type* {{%.*}}, i8** [[TEST_TUPLE_HASHABLE_WT1]], {{%.*}} noalias nocapture swiftself undef)
87+
let _ = Wrapper(value: ()).hashValue
88+
}

0 commit comments

Comments
 (0)