Skip to content

Commit e60ef84

Browse files
committed
Implement Tuple Equatable Conformance
1 parent 4b71d67 commit e60ef84

15 files changed

+464
-23
lines changed
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
//===--- BuiltinProtocolWitnessTable.h --------------------------*- C++ -*-===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2020 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
//
13+
// Swift runtime support for builtin protocol witnesses and related items.
14+
//
15+
//===----------------------------------------------------------------------===//
16+
17+
#ifndef SWIFT_RUNTIME_BUILTINPROTOCOLCONFORMANCES_H
18+
#define SWIFT_RUNTIME_BUILTINPROTOCOLCONFORMANCES_H
19+
20+
#include "swift/ABI/Metadata.h"
21+
22+
namespace swift {
23+
24+
#define STR(a) #a
25+
#define XSTR(a) STR(a)
26+
#define SYMBOL(name) XSTR(__USER_LABEL_PREFIX__) name
27+
28+
// public protocol Equatable {}
29+
#define SWIFT_EQUATABLE_MANGLING SQ
30+
31+
#define PROTOCOL_DESCRIPTOR_MANGLING Mp
32+
33+
#define PROTOCOL_DESCRIPTOR_SYM(Proto) \
34+
MANGLE_SYM(MANGLING_CONCAT2(Proto, PROTOCOL_DESCRIPTOR_MANGLING))
35+
36+
#define EQUATABLE_PROTOCOL_DESCRIPTOR \
37+
PROTOCOL_DESCRIPTOR_SYM(SWIFT_EQUATABLE_MANGLING)
38+
39+
#define TUPLE_EQUATABLE_CONF SYMBOL("_swift_tupleEquatable_conf")
40+
#define TUPLE_EQUATABLE_EQUALS SYMBOL("_swift_tupleEquatable_equals")
41+
42+
/// The protocol witness for static Swift.Equatable.== infix(A, A) -> Swift.Bool
43+
/// in conformance (A...): Swift.Equatable in Swift.
44+
SWIFT_RUNTIME_EXPORT SWIFT_CC(swift)
45+
bool _swift_tupleEquatable_equals(OpaqueValue *tuple1, OpaqueValue *tuple2,
46+
SWIFT_CONTEXT Metadata *swiftSelf,
47+
Metadata *Self, void *witnessTable);
48+
49+
} // end namespace swift
50+
51+
#endif

lib/AST/Module.cpp

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//
33
// This source file is part of the Swift.org open source project
44
//
5-
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
5+
// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
66
// Licensed under Apache License v2.0 with Runtime Library Exception
77
//
88
// See https://swift.org/LICENSE.txt for license information
@@ -1013,6 +1013,28 @@ LookupConformanceInModuleRequest::evaluate(
10131013
if (type->is<UnresolvedType>())
10141014
return ProtocolConformanceRef(protocol);
10151015

1016+
// Tuples have builtin conformances implemented within the runtime.
1017+
// These conformances so far consist of Equatable.
1018+
if (auto tuple = type->getAs<TupleType>()) {
1019+
if (protocol == ctx.getProtocol(KnownProtocolKind::Equatable)) {
1020+
SmallVector<ProtocolConformanceRef, 4> elementConformances;
1021+
1022+
// Ensure that every element in this tuple conforms to Equatable.
1023+
for (auto eltTy : tuple->getElementTypes()) {
1024+
auto conformance = mod->lookupConformance(eltTy, protocol);
1025+
1026+
if (conformance.isInvalid())
1027+
return ProtocolConformanceRef::forInvalid();
1028+
1029+
elementConformances.push_back(conformance);
1030+
}
1031+
1032+
auto conformance = ctx.getBuiltinConformance(tuple, protocol,
1033+
elementConformances);
1034+
return ProtocolConformanceRef(conformance);
1035+
}
1036+
}
1037+
10161038
auto nominal = type->getAnyNominal();
10171039

10181040
// If we don't have a nominal type, there are no conformances.

lib/IRGen/GenProto.cpp

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -906,6 +906,16 @@ static bool isDependentConformance(
906906
IRGenModule &IGM,
907907
const RootProtocolConformance *rootConformance,
908908
llvm::SmallPtrSet<const NormalProtocolConformance *, 4> &visited){
909+
// Some Builtin conformances are dependent.
910+
if (auto builtin = dyn_cast<BuiltinProtocolConformance>(rootConformance)) {
911+
// Tuples are conditionally conformed to any builtin conformance.
912+
if (builtin->getType()->is<TupleType>())
913+
return true;
914+
915+
// Otherwise, this builtin conformance is not dependant.
916+
return false;
917+
}
918+
909919
// Self-conformances are never dependent.
910920
auto conformance = dyn_cast<NormalProtocolConformance>(rootConformance);
911921
if (!conformance)
@@ -971,12 +981,10 @@ static bool isSynthesizedNonUnique(const RootProtocolConformance *conformance) {
971981
static llvm::Value *
972982
emitConditionalConformancesBuffer(IRGenFunction &IGF,
973983
const ProtocolConformance *substConformance) {
974-
auto rootConformance =
975-
dyn_cast<NormalProtocolConformance>(substConformance->getRootConformance());
984+
auto rootConformance = substConformance->getRootConformance();
976985

977-
// Not a normal conformance means no conditional requirements means no need
978-
// for a buffer.
979-
if (!rootConformance)
986+
if (!isa<NormalProtocolConformance>(rootConformance) &&
987+
!isa<BuiltinProtocolConformance>(rootConformance))
980988
return llvm::UndefValue::get(IGF.IGM.WitnessTablePtrPtrTy);
981989

982990
// Pointers to the witness tables, in the right order, which will be included
@@ -986,9 +994,18 @@ emitConditionalConformancesBuffer(IRGenFunction &IGF,
986994
auto subMap = substConformance->getSubstitutions(IGF.IGM.getSwiftModule());
987995

988996
SILWitnessTable::enumerateWitnessTableConditionalConformances(
989-
rootConformance, [&](unsigned, CanType type, ProtocolDecl *proto) {
997+
rootConformance, [&](unsigned i, CanType type, ProtocolDecl *proto) {
990998
auto substType = type.subst(subMap)->getCanonicalType();
991999
auto reqConformance = subMap.lookupConformance(type, proto);
1000+
1001+
// Builtin conformances don't have a substitution list, so accomodate
1002+
// for that here.
1003+
if (auto builtin
1004+
= dyn_cast<BuiltinProtocolConformance>(rootConformance)) {
1005+
substType = type->getCanonicalType();
1006+
reqConformance = builtin->getConformances()[i];
1007+
}
1008+
9921009
assert(reqConformance && "conditional conformance must be valid");
9931010

9941011
tables.push_back(emitWitnessTableRef(IGF, substType, reqConformance));
@@ -1137,8 +1154,10 @@ class AccessorConformanceInfo : public ConformanceInfo {
11371154
llvm::Value *getTable(IRGenFunction &IGF,
11381155
llvm::Value **typeMetadataCache) const override {
11391156
// If we're looking up a dependent type, we can't cache the result.
1157+
// If the conformance is builtin, go ahead and emit an accessor call.
11401158
if (Conformance->getType()->hasArchetype() ||
1141-
Conformance->getType()->hasDynamicSelfType()) {
1159+
Conformance->getType()->hasDynamicSelfType() ||
1160+
isa<BuiltinProtocolConformance>(Conformance)) {
11421161
return emitWitnessTableAccessorCall(IGF, Conformance,
11431162
typeMetadataCache);
11441163
}

lib/IRGen/IRGenMangler.cpp

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//
33
// This source file is part of the Swift.org open source project
44
//
5-
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
5+
// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
66
// Licensed under Apache License v2.0 with Runtime Library Exception
77
//
88
// See https://swift.org/LICENSE.txt for license information
@@ -147,6 +147,26 @@ IRGenMangler::mangleTypeForReflection(IRGenModule &IGM,
147147

148148
std::string IRGenMangler::mangleProtocolConformanceDescriptor(
149149
const RootProtocolConformance *conformance) {
150+
// Builtin conformances are different because they don't use a mangled name
151+
// for their conformance descriptors. For now, we use predefined symbol names
152+
// just in case in the future we have some conflict between actual
153+
// conformances and these builtin ones.
154+
if (isa<BuiltinProtocolConformance>(conformance)) {
155+
auto &ctx = conformance->getType()->getASTContext();
156+
157+
if (conformance->getType()->is<TupleType>()) {
158+
auto equatable = ctx.getProtocol(KnownProtocolKind::Equatable);
159+
160+
if (conformance->getProtocol() == equatable) {
161+
return "_swift_tupleEquatable_conf";
162+
}
163+
164+
llvm_unreachable("mangling unknown tuple witness table protocol");
165+
}
166+
167+
llvm_unreachable("mangling unknown builtin witness table type");
168+
}
169+
150170
beginMangling();
151171
if (isa<NormalProtocolConformance>(conformance)) {
152172
appendProtocolConformance(conformance);

lib/IRGen/IRGenModule.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//
33
// This source file is part of the Swift.org open source project
44
//
5-
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
5+
// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
66
// Licensed under Apache License v2.0 with Runtime Library Exception
77
//
88
// See https://swift.org/LICENSE.txt for license information
@@ -972,6 +972,11 @@ bool IRGenerator::canEmitWitnessTableLazily(SILWitnessTable *wt) {
972972
if (wt->getLinkage() == SILLinkage::Shared)
973973
return true;
974974

975+
// If we happen to see a builtin witness table here, we can't emit those.
976+
// The runtime has those for us.
977+
if (isa<BuiltinProtocolConformance>(wt->getConformance()))
978+
return false;
979+
975980
NominalTypeDecl *ConformingTy =
976981
wt->getConformingType()->getNominalOrBoundGenericNominal();
977982

lib/SIL/IR/SIL.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//
33
// This source file is part of the Swift.org open source project
44
//
5-
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
5+
// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
66
// Licensed under Apache License v2.0 with Runtime Library Exception
77
//
88
// See https://swift.org/LICENSE.txt for license information
@@ -80,6 +80,11 @@ swift::getLinkageForProtocolConformance(const RootProtocolConformance *C,
8080
if (isa<ClangModuleUnit>(C->getDeclContext()->getModuleScopeContext()))
8181
return SILLinkage::Shared;
8282

83+
// If the conforming type is a non-nomianl, give it public linkage.
84+
// These conformances are implemented within the runtime.
85+
if (!C->getType()->getAnyNominal())
86+
return definition ? SILLinkage::Public : SILLinkage::PublicExternal;
87+
8388
auto typeDecl = C->getType()->getNominalOrBoundGenericNominal();
8489
AccessLevel access = std::min(C->getProtocol()->getEffectiveAccess(),
8590
typeDecl->getEffectiveAccess());
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
//===--- BuiltinProtocolConformances.cpp ----------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2020 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
//
13+
// Definitions of some builtin protocol witnesses.
14+
//
15+
//===----------------------------------------------------------------------===//
16+
17+
#include "swift/Runtime/BuiltinProtocolConformances.h"
18+
#include "swift/Runtime/Casting.h"
19+
#include "swift/Runtime/Debug.h"
20+
#include "swift/Runtime/Metadata.h"
21+
22+
using namespace swift;
23+
24+
extern const ProtocolDescriptor
25+
PROTOCOL_DESCRIPTOR_SYM(SWIFT_EQUATABLE_MANGLING);
26+
27+
#if defined(__ELF__)
28+
// Create a GOT equivalent for the Equatable reference.
29+
__asm(
30+
" .type got.$sSQMp, @object\n"
31+
" .section .data.rel.ro\n"
32+
" .p2align 3\n"
33+
"got.$sSQMp:\n"
34+
" .quad ($sSQMp)\n"
35+
" .size got.$sSQMp, 8\n"
36+
);
37+
38+
// Create a GOT equivalent for the Equatable.== method descriptor.
39+
__asm(
40+
" .type got.$sSQ2eeoiySbx_xtFZTq, @object\n"
41+
" .p2align 3\n"
42+
"got.$sSQ2eeoiySbx_xtFZTq:\n"
43+
" .quad ($sSQ2eeoiySbx_xtFZTq)\n"
44+
" .size got.$sSQ2eeoiySbx_xtFZTq, 8\n"
45+
);
46+
#endif
47+
48+
// Define the conformance descriptor for tuple Equatable. We do this in
49+
// assembly to work around relative reference issues.
50+
__asm(
51+
#if defined(__ELF__)
52+
" .type __swift_tupleEquatable_private, @object\n"
53+
" .local __swift_tupleEquatable_private\n"
54+
" .comm __swift_tupleEquatable_private, 128, 16\n"
55+
" .protected " TUPLE_EQUATABLE_CONF "\n"
56+
" .type " TUPLE_EQUATABLE_CONF ", @object\n"
57+
" .section .rodata\n"
58+
#elif defined(__MACH__)
59+
" .zerofill __DATA, __bss, __swift_tupleEquatable_private, 128, 4\n"
60+
" .section __TEXT, __const\n"
61+
#endif
62+
" .globl " TUPLE_EQUATABLE_CONF "\n"
63+
" .p2align 2\n"
64+
TUPLE_EQUATABLE_CONF ":\n"
65+
#if defined(__ELF__)
66+
// This is an indirectable relative reference to the GOT equivalent for the
67+
// Equatable protocol descriptor, hence why we add 1 to indicate indirect.
68+
" .long (got.$sSQMp - (" TUPLE_EQUATABLE_CONF ")) + 1\n"
69+
#elif defined(__MACH__)
70+
" .long _$sSQMp@GOTPCREL + 5\n"
71+
#endif
72+
// 769 is the MetadataKind::Tuple
73+
" .long 769\n"
74+
// This indicates that we have no witness table pattern. We use a generic
75+
// witness table for builtin conformances.
76+
" .long 0\n"
77+
// 196640 are the ConformanceFlags with the type reference bit set to
78+
// MetadataKind, the has resilient witness bit, and the generic witness table
79+
// bit.
80+
" .long 196640\n"
81+
// This 1 is the ResilientWitnessesHeader indicating we have 1 resilient
82+
// witness.
83+
" .long 1\n"
84+
#if defined(__ELF__)
85+
// This is an indirectable relative reference to the GOT equivalent for the
86+
// Equatable.== method descriptor, hence why we add 1 to indicate indirect.
87+
" .long ((got.$sSQ2eeoiySbx_xtFZTq - (" TUPLE_EQUATABLE_CONF ")) - 20) + 1\n"
88+
#elif defined(__MACH__)
89+
" .long _$sSQ2eeoiySbx_xtFZTq@GOTPCREL + 5\n"
90+
#endif
91+
// This is a direct relative reference to the equals witness defined below.
92+
" .long ((" TUPLE_EQUATABLE_EQUALS ") - (" TUPLE_EQUATABLE_CONF ")) - 24\n"
93+
// The witness table size in words.
94+
" .short 0\n"
95+
// The witness table private size in words & requires instantiation.
96+
" .short 1\n"
97+
// The witness table instantiator function.
98+
" .long 0\n"
99+
// This is a direct relative reference to the private data for the
100+
// conformance.
101+
" .long (__swift_tupleEquatable_private - (" TUPLE_EQUATABLE_CONF ")) - 36\n"
102+
#if defined(__ELF__)
103+
" .size " TUPLE_EQUATABLE_CONF ", 40\n"
104+
#endif
105+
);
106+
107+
SWIFT_RUNTIME_EXPORT SWIFT_CC(swift)
108+
bool swift::_swift_tupleEquatable_equals(OpaqueValue *tuple1,
109+
OpaqueValue *tuple2,
110+
SWIFT_CONTEXT Metadata *swiftSelf,
111+
Metadata *Self, void *witnessTable) {
112+
auto tuple = cast<TupleTypeMetadata>(Self);
113+
auto table = reinterpret_cast<void**>(witnessTable);
114+
115+
// Loop through all elements, and check if both tuples element is equal.
116+
for (size_t i = 0; i != tuple->NumElements; i += 1) {
117+
auto elt = tuple->getElement(i);
118+
119+
// Get the element conformance from the private data in the witness table.
120+
auto conformance = reinterpret_cast<const WitnessTable *>(table[-1 - i]);
121+
122+
// Get the respective values from both tuples.
123+
auto value1 = reinterpret_cast<OpaqueValue *>(
124+
reinterpret_cast<char *>(tuple1) + elt.Offset);
125+
auto value2 = reinterpret_cast<OpaqueValue *>(
126+
reinterpret_cast<char *>(tuple2) + elt.Offset);
127+
128+
// Grab the specific witness for this element type.
129+
auto equatableTable = reinterpret_cast<void * const *>(conformance);
130+
auto equalsWitness = equatableTable[WitnessTableFirstRequirementOffset];
131+
using Fn = SWIFT_CC(swift) bool(OpaqueValue *, OpaqueValue *,
132+
SWIFT_CONTEXT const Metadata *,
133+
const Metadata *, const WitnessTable *);
134+
auto equals = reinterpret_cast<Fn *>(equalsWitness);
135+
136+
// Call the equal function
137+
auto result = equals(value1, value2, elt.Type, elt.Type, conformance);
138+
139+
// If the values aren't equal, this tuple isn't equal. :)
140+
if (!result)
141+
return false;
142+
}
143+
144+
// Otherwise this tuple has value equality with all elements.
145+
return true;
146+
}

stdlib/public/runtime/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ set(swift_runtime_sources
2929
AnyHashableSupport.cpp
3030
Array.cpp
3131
BackDeployment.cpp
32+
BuiltinProtocolConformances.cpp
3233
Casting.cpp
3334
CompatibilityOverride.cpp
3435
CygwinPort.cpp

0 commit comments

Comments
 (0)