Skip to content

Commit

Permalink
Implement Tuple Equatable Conformance
Browse files Browse the repository at this point in the history
  • Loading branch information
Azoy committed Oct 22, 2020
1 parent 4b71d67 commit e60ef84
Show file tree
Hide file tree
Showing 15 changed files with 464 additions and 23 deletions.
51 changes: 51 additions & 0 deletions include/swift/Runtime/BuiltinProtocolConformances.h
@@ -0,0 +1,51 @@
//===--- BuiltinProtocolWitnessTable.h --------------------------*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2020 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// Swift runtime support for builtin protocol witnesses and related items.
//
//===----------------------------------------------------------------------===//

#ifndef SWIFT_RUNTIME_BUILTINPROTOCOLCONFORMANCES_H
#define SWIFT_RUNTIME_BUILTINPROTOCOLCONFORMANCES_H

#include "swift/ABI/Metadata.h"

namespace swift {

#define STR(a) #a
#define XSTR(a) STR(a)
#define SYMBOL(name) XSTR(__USER_LABEL_PREFIX__) name

// public protocol Equatable {}
#define SWIFT_EQUATABLE_MANGLING SQ

#define PROTOCOL_DESCRIPTOR_MANGLING Mp

#define PROTOCOL_DESCRIPTOR_SYM(Proto) \
MANGLE_SYM(MANGLING_CONCAT2(Proto, PROTOCOL_DESCRIPTOR_MANGLING))

#define EQUATABLE_PROTOCOL_DESCRIPTOR \
PROTOCOL_DESCRIPTOR_SYM(SWIFT_EQUATABLE_MANGLING)

#define TUPLE_EQUATABLE_CONF SYMBOL("_swift_tupleEquatable_conf")
#define TUPLE_EQUATABLE_EQUALS SYMBOL("_swift_tupleEquatable_equals")

/// The protocol witness for static Swift.Equatable.== infix(A, A) -> Swift.Bool
/// in conformance (A...): Swift.Equatable in Swift.
SWIFT_RUNTIME_EXPORT SWIFT_CC(swift)
bool _swift_tupleEquatable_equals(OpaqueValue *tuple1, OpaqueValue *tuple2,
SWIFT_CONTEXT Metadata *swiftSelf,
Metadata *Self, void *witnessTable);

} // end namespace swift

#endif
24 changes: 23 additions & 1 deletion lib/AST/Module.cpp
Expand Up @@ -2,7 +2,7 @@
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
Expand Down Expand Up @@ -1013,6 +1013,28 @@ LookupConformanceInModuleRequest::evaluate(
if (type->is<UnresolvedType>())
return ProtocolConformanceRef(protocol);

// Tuples have builtin conformances implemented within the runtime.
// These conformances so far consist of Equatable.
if (auto tuple = type->getAs<TupleType>()) {
if (protocol == ctx.getProtocol(KnownProtocolKind::Equatable)) {
SmallVector<ProtocolConformanceRef, 4> elementConformances;

// Ensure that every element in this tuple conforms to Equatable.
for (auto eltTy : tuple->getElementTypes()) {
auto conformance = mod->lookupConformance(eltTy, protocol);

if (conformance.isInvalid())
return ProtocolConformanceRef::forInvalid();

elementConformances.push_back(conformance);
}

auto conformance = ctx.getBuiltinConformance(tuple, protocol,
elementConformances);
return ProtocolConformanceRef(conformance);
}
}

auto nominal = type->getAnyNominal();

// If we don't have a nominal type, there are no conformances.
Expand Down
33 changes: 26 additions & 7 deletions lib/IRGen/GenProto.cpp
Expand Up @@ -906,6 +906,16 @@ static bool isDependentConformance(
IRGenModule &IGM,
const RootProtocolConformance *rootConformance,
llvm::SmallPtrSet<const NormalProtocolConformance *, 4> &visited){
// Some Builtin conformances are dependent.
if (auto builtin = dyn_cast<BuiltinProtocolConformance>(rootConformance)) {
// Tuples are conditionally conformed to any builtin conformance.
if (builtin->getType()->is<TupleType>())
return true;

// Otherwise, this builtin conformance is not dependant.
return false;
}

// Self-conformances are never dependent.
auto conformance = dyn_cast<NormalProtocolConformance>(rootConformance);
if (!conformance)
Expand Down Expand Up @@ -971,12 +981,10 @@ static bool isSynthesizedNonUnique(const RootProtocolConformance *conformance) {
static llvm::Value *
emitConditionalConformancesBuffer(IRGenFunction &IGF,
const ProtocolConformance *substConformance) {
auto rootConformance =
dyn_cast<NormalProtocolConformance>(substConformance->getRootConformance());
auto rootConformance = substConformance->getRootConformance();

// Not a normal conformance means no conditional requirements means no need
// for a buffer.
if (!rootConformance)
if (!isa<NormalProtocolConformance>(rootConformance) &&
!isa<BuiltinProtocolConformance>(rootConformance))
return llvm::UndefValue::get(IGF.IGM.WitnessTablePtrPtrTy);

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

SILWitnessTable::enumerateWitnessTableConditionalConformances(
rootConformance, [&](unsigned, CanType type, ProtocolDecl *proto) {
rootConformance, [&](unsigned i, CanType type, ProtocolDecl *proto) {
auto substType = type.subst(subMap)->getCanonicalType();
auto reqConformance = subMap.lookupConformance(type, proto);

// Builtin conformances don't have a substitution list, so accomodate
// for that here.
if (auto builtin
= dyn_cast<BuiltinProtocolConformance>(rootConformance)) {
substType = type->getCanonicalType();
reqConformance = builtin->getConformances()[i];
}

assert(reqConformance && "conditional conformance must be valid");

tables.push_back(emitWitnessTableRef(IGF, substType, reqConformance));
Expand Down Expand Up @@ -1137,8 +1154,10 @@ class AccessorConformanceInfo : public ConformanceInfo {
llvm::Value *getTable(IRGenFunction &IGF,
llvm::Value **typeMetadataCache) const override {
// If we're looking up a dependent type, we can't cache the result.
// If the conformance is builtin, go ahead and emit an accessor call.
if (Conformance->getType()->hasArchetype() ||
Conformance->getType()->hasDynamicSelfType()) {
Conformance->getType()->hasDynamicSelfType() ||
isa<BuiltinProtocolConformance>(Conformance)) {
return emitWitnessTableAccessorCall(IGF, Conformance,
typeMetadataCache);
}
Expand Down
22 changes: 21 additions & 1 deletion lib/IRGen/IRGenMangler.cpp
Expand Up @@ -2,7 +2,7 @@
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
Expand Down Expand Up @@ -147,6 +147,26 @@ IRGenMangler::mangleTypeForReflection(IRGenModule &IGM,

std::string IRGenMangler::mangleProtocolConformanceDescriptor(
const RootProtocolConformance *conformance) {
// Builtin conformances are different because they don't use a mangled name
// for their conformance descriptors. For now, we use predefined symbol names
// just in case in the future we have some conflict between actual
// conformances and these builtin ones.
if (isa<BuiltinProtocolConformance>(conformance)) {
auto &ctx = conformance->getType()->getASTContext();

if (conformance->getType()->is<TupleType>()) {
auto equatable = ctx.getProtocol(KnownProtocolKind::Equatable);

if (conformance->getProtocol() == equatable) {
return "_swift_tupleEquatable_conf";
}

llvm_unreachable("mangling unknown tuple witness table protocol");
}

llvm_unreachable("mangling unknown builtin witness table type");
}

beginMangling();
if (isa<NormalProtocolConformance>(conformance)) {
appendProtocolConformance(conformance);
Expand Down
7 changes: 6 additions & 1 deletion lib/IRGen/IRGenModule.cpp
Expand Up @@ -2,7 +2,7 @@
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
Expand Down Expand Up @@ -972,6 +972,11 @@ bool IRGenerator::canEmitWitnessTableLazily(SILWitnessTable *wt) {
if (wt->getLinkage() == SILLinkage::Shared)
return true;

// If we happen to see a builtin witness table here, we can't emit those.
// The runtime has those for us.
if (isa<BuiltinProtocolConformance>(wt->getConformance()))
return false;

NominalTypeDecl *ConformingTy =
wt->getConformingType()->getNominalOrBoundGenericNominal();

Expand Down
7 changes: 6 additions & 1 deletion lib/SIL/IR/SIL.cpp
Expand Up @@ -2,7 +2,7 @@
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
Expand Down Expand Up @@ -80,6 +80,11 @@ swift::getLinkageForProtocolConformance(const RootProtocolConformance *C,
if (isa<ClangModuleUnit>(C->getDeclContext()->getModuleScopeContext()))
return SILLinkage::Shared;

// If the conforming type is a non-nomianl, give it public linkage.
// These conformances are implemented within the runtime.
if (!C->getType()->getAnyNominal())
return definition ? SILLinkage::Public : SILLinkage::PublicExternal;

auto typeDecl = C->getType()->getNominalOrBoundGenericNominal();
AccessLevel access = std::min(C->getProtocol()->getEffectiveAccess(),
typeDecl->getEffectiveAccess());
Expand Down
146 changes: 146 additions & 0 deletions stdlib/public/runtime/BuiltinProtocolConformances.cpp
@@ -0,0 +1,146 @@
//===--- BuiltinProtocolConformances.cpp ----------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2020 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// Definitions of some builtin protocol witnesses.
//
//===----------------------------------------------------------------------===//

#include "swift/Runtime/BuiltinProtocolConformances.h"
#include "swift/Runtime/Casting.h"
#include "swift/Runtime/Debug.h"
#include "swift/Runtime/Metadata.h"

using namespace swift;

extern const ProtocolDescriptor
PROTOCOL_DESCRIPTOR_SYM(SWIFT_EQUATABLE_MANGLING);

#if defined(__ELF__)
// Create a GOT equivalent for the Equatable reference.
__asm(
" .type got.$sSQMp, @object\n"
" .section .data.rel.ro\n"
" .p2align 3\n"
"got.$sSQMp:\n"
" .quad ($sSQMp)\n"
" .size got.$sSQMp, 8\n"
);

// Create a GOT equivalent for the Equatable.== method descriptor.
__asm(
" .type got.$sSQ2eeoiySbx_xtFZTq, @object\n"
" .p2align 3\n"
"got.$sSQ2eeoiySbx_xtFZTq:\n"
" .quad ($sSQ2eeoiySbx_xtFZTq)\n"
" .size got.$sSQ2eeoiySbx_xtFZTq, 8\n"
);
#endif

// Define the conformance descriptor for tuple Equatable. We do this in
// assembly to work around relative reference issues.
__asm(
#if defined(__ELF__)
" .type __swift_tupleEquatable_private, @object\n"
" .local __swift_tupleEquatable_private\n"
" .comm __swift_tupleEquatable_private, 128, 16\n"
" .protected " TUPLE_EQUATABLE_CONF "\n"
" .type " TUPLE_EQUATABLE_CONF ", @object\n"
" .section .rodata\n"
#elif defined(__MACH__)
" .zerofill __DATA, __bss, __swift_tupleEquatable_private, 128, 4\n"
" .section __TEXT, __const\n"
#endif
" .globl " TUPLE_EQUATABLE_CONF "\n"
" .p2align 2\n"
TUPLE_EQUATABLE_CONF ":\n"
#if defined(__ELF__)
// This is an indirectable relative reference to the GOT equivalent for the
// Equatable protocol descriptor, hence why we add 1 to indicate indirect.
" .long (got.$sSQMp - (" TUPLE_EQUATABLE_CONF ")) + 1\n"
#elif defined(__MACH__)
" .long _$sSQMp@GOTPCREL + 5\n"
#endif
// 769 is the MetadataKind::Tuple
" .long 769\n"
// This indicates that we have no witness table pattern. We use a generic
// witness table for builtin conformances.
" .long 0\n"
// 196640 are the ConformanceFlags with the type reference bit set to
// MetadataKind, the has resilient witness bit, and the generic witness table
// bit.
" .long 196640\n"
// This 1 is the ResilientWitnessesHeader indicating we have 1 resilient
// witness.
" .long 1\n"
#if defined(__ELF__)
// This is an indirectable relative reference to the GOT equivalent for the
// Equatable.== method descriptor, hence why we add 1 to indicate indirect.
" .long ((got.$sSQ2eeoiySbx_xtFZTq - (" TUPLE_EQUATABLE_CONF ")) - 20) + 1\n"
#elif defined(__MACH__)
" .long _$sSQ2eeoiySbx_xtFZTq@GOTPCREL + 5\n"
#endif
// This is a direct relative reference to the equals witness defined below.
" .long ((" TUPLE_EQUATABLE_EQUALS ") - (" TUPLE_EQUATABLE_CONF ")) - 24\n"
// The witness table size in words.
" .short 0\n"
// The witness table private size in words & requires instantiation.
" .short 1\n"
// The witness table instantiator function.
" .long 0\n"
// This is a direct relative reference to the private data for the
// conformance.
" .long (__swift_tupleEquatable_private - (" TUPLE_EQUATABLE_CONF ")) - 36\n"
#if defined(__ELF__)
" .size " TUPLE_EQUATABLE_CONF ", 40\n"
#endif
);

SWIFT_RUNTIME_EXPORT SWIFT_CC(swift)
bool swift::_swift_tupleEquatable_equals(OpaqueValue *tuple1,
OpaqueValue *tuple2,
SWIFT_CONTEXT Metadata *swiftSelf,
Metadata *Self, void *witnessTable) {
auto tuple = cast<TupleTypeMetadata>(Self);
auto table = reinterpret_cast<void**>(witnessTable);

// Loop through all elements, and check if both tuples element is equal.
for (size_t i = 0; i != tuple->NumElements; i += 1) {
auto elt = tuple->getElement(i);

// Get the element conformance from the private data in the witness table.
auto conformance = reinterpret_cast<const WitnessTable *>(table[-1 - i]);

// Get the respective values from both tuples.
auto value1 = reinterpret_cast<OpaqueValue *>(
reinterpret_cast<char *>(tuple1) + elt.Offset);
auto value2 = reinterpret_cast<OpaqueValue *>(
reinterpret_cast<char *>(tuple2) + elt.Offset);

// Grab the specific witness for this element type.
auto equatableTable = reinterpret_cast<void * const *>(conformance);
auto equalsWitness = equatableTable[WitnessTableFirstRequirementOffset];
using Fn = SWIFT_CC(swift) bool(OpaqueValue *, OpaqueValue *,
SWIFT_CONTEXT const Metadata *,
const Metadata *, const WitnessTable *);
auto equals = reinterpret_cast<Fn *>(equalsWitness);

// Call the equal function
auto result = equals(value1, value2, elt.Type, elt.Type, conformance);

// If the values aren't equal, this tuple isn't equal. :)
if (!result)
return false;
}

// Otherwise this tuple has value equality with all elements.
return true;
}
1 change: 1 addition & 0 deletions stdlib/public/runtime/CMakeLists.txt
Expand Up @@ -29,6 +29,7 @@ set(swift_runtime_sources
AnyHashableSupport.cpp
Array.cpp
BackDeployment.cpp
BuiltinProtocolConformances.cpp
Casting.cpp
CompatibilityOverride.cpp
CygwinPort.cpp
Expand Down

0 comments on commit e60ef84

Please sign in to comment.