Skip to content

Commit

Permalink
[Compatibility53] Backport Tuple Comparable Conformance
Browse files Browse the repository at this point in the history
Add protocol witnesses for Comparable requirements

remove unused table
  • Loading branch information
Azoy committed Oct 22, 2020
1 parent fd950eb commit 13b5bf4
Show file tree
Hide file tree
Showing 2 changed files with 341 additions and 16 deletions.
336 changes: 325 additions & 11 deletions stdlib/toolchain/Compatibility53/BuiltinProtocolConformances.cpp
Expand Up @@ -30,6 +30,13 @@ static const ProtocolDescriptor *getEquatableDescriptor() {
return descriptor;
}

static const ProtocolDescriptor *getComparableDescriptor() {
auto descriptor = SWIFT_LAZY_CONSTANT(
reinterpret_cast<const ProtocolDescriptor *>(
dlsym(RTLD_DEFAULT, "$sSLMp")));
return descriptor;
}

static const WitnessTable *conformsToProtocol(const Metadata *type,
const ProtocolDescriptor *protocol) {
using Fn = const WitnessTable *(const Metadata *, const ProtocolDescriptor *);
Expand All @@ -39,6 +46,21 @@ static const WitnessTable *conformsToProtocol(const Metadata *type,
return func(type, protocol);
}

template<unsigned int NumWitnesses>
struct _WitnessTable {
const ProtocolConformanceDescriptor *Conformance;
const void *Witnesses[NumWitnesses];
};

using StaticInfixWitness = SWIFT_CC(swift) bool(OpaqueValue *, OpaqueValue *,
SWIFT_CONTEXT const Metadata *,
const Metadata *,
const WitnessTable *);

//===----------------------------------------------------------------------===//
// Tuple Equatable Conformance
//===----------------------------------------------------------------------===//

#define TUPLE_EQUATABLE_WT SYMBOL("_swift_tupleEquatable_wt")

// Define the conformance descriptor for tuple Equatable. We do this in
Expand Down Expand Up @@ -66,7 +88,7 @@ extern const ProtocolConformanceDescriptor _swift_tupleEquatable_conf;
// dependency to libswiftCore (which is where the Equatable protocol desciptor
// lives), we have to manually implant this before calling any user code.
__attribute__((constructor))
void _emplaceEquatableDescriptor() {
void _emplaceTupleEquatableDescriptor() {
auto tupleEquatableConf = const_cast<int32_t *>(
reinterpret_cast<const int32_t *>(&_swift_tupleEquatable_conf));
auto equatable = getEquatableDescriptor();
Expand All @@ -75,12 +97,6 @@ void _emplaceEquatableDescriptor() {
*tupleEquatableConf = intptr_t(equatable) - intptr_t(tupleEquatableConf);
}

template<unsigned int NumWitnesses>
struct _WitnessTable {
const ProtocolConformanceDescriptor *Conformance;
const void *Witnesses[NumWitnesses];
};

SWIFT_RUNTIME_EXPORT
const _WitnessTable<1> _swift_tupleEquatable_wt = {
&_swift_tupleEquatable_conf,
Expand Down Expand Up @@ -119,10 +135,7 @@ bool swift::_swift_tupleEquatable_equals(OpaqueValue *tuple1,
// 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);
auto equals = reinterpret_cast<StaticInfixWitness *>(equalsWitness);

// Call the equal function.
auto result = equals(value1, value2, elt.Type, elt.Type, conformance);
Expand All @@ -135,3 +148,304 @@ bool swift::_swift_tupleEquatable_equals(OpaqueValue *tuple1,
// Otherwise this tuple has value equality with all elements.
return true;
}

//===----------------------------------------------------------------------===//
// Tuple Comparable Conformance
//===----------------------------------------------------------------------===//

#define TUPLE_COMPARABLE_WT SYMBOL("_swift_tupleComparable_wt")

// Define the conformance descriptor for tuple Comparable. We do this in
// assembly to work around relative reference issues.
__asm(
" .section __DATA,__data\n"
" .globl " TUPLE_COMPARABLE_CONF "\n"
" .p2align 2\n"
TUPLE_COMPARABLE_CONF ":\n"
// This is an indirectable relative reference to the Comparable protocol
// descriptor. However, this is 0 here because the compatibility libraries
// can't have a dependency on libswiftCore (which is where Comparable lives).
" .long 0\n"
// 769 is the MetadataKind::Tuple
" .long 769\n"
// This is a direct relative reference to the witness table defined below.
" .long ((" TUPLE_COMPARABLE_WT ") - (" TUPLE_COMPARABLE_CONF ")) - 8\n"
// 32 are the ConformanceFlags with the type reference bit set to MetadataKind.
" .long 32\n"
);

extern const ProtocolConformanceDescriptor _swift_tupleComparable_conf;

// Due to the fact that the compatibility libraries can't have a hard
// dependency to libswiftCore (which is where the Comparable protocol desciptor
// lives), we have to manually implant this before calling any user code.
__attribute__((constructor))
void _emplaceTupleComparableDescriptor() {
auto tupleComparableConf = const_cast<int32_t *>(
reinterpret_cast<const int32_t *>(&_swift_tupleComparable_conf));
auto comparable = getComparableDescriptor();

// This is an indirectable pointer.
*tupleComparableConf = intptr_t(comparable) - intptr_t(tupleComparableConf);
}

// The base Equatable protocol is itself a requirement, thus the requirement
// count is 5 (Equatable + 4 operators) and the witness is the tuple Equatable
// table.
SWIFT_RUNTIME_EXPORT
const _WitnessTable<5> _swift_tupleComparable_wt = {
&_swift_tupleComparable_conf,
{
reinterpret_cast<const void *>(&_swift_tupleEquatable_wt),
reinterpret_cast<void *>(_swift_tupleComparable_lessThan),
reinterpret_cast<void *>(_swift_tupleComparable_lessThanOrEqual),
reinterpret_cast<void *>(_swift_tupleComparable_greaterThanOrEqual),
reinterpret_cast<void *>(_swift_tupleComparable_greaterThan)
}
};

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

// 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);

// Ensure we actually have a conformance to Comparable for this element type.
auto comparable = getComparableDescriptor();
auto conformance = conformsToProtocol(elt.Type, comparable);

// If we don't have a conformance then something somewhere messed up in
// deciding that this tuple type was Comparable...??
if (!conformance)
swift_unreachable("Tuple comparability requires that all elements \
be Comparable.");

// 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);

// First, grab the equatable conformance to prepare to check if the elements
// are equal. (Since this require Equatable conformance, the witness table
// is right after the conformance descriptor, which is at index 0.)
auto comparableTable = reinterpret_cast<void * const *>(conformance);
auto equatableTable = reinterpret_cast<void * const *>(comparableTable[1]);
auto equalsWitness = equatableTable[WitnessTableFirstRequirementOffset];
auto equals = reinterpret_cast<StaticInfixWitness *>(equalsWitness);

// Call the equal function.
auto isEqual = equals(value1, value2, elt.Type, elt.Type,
reinterpret_cast<const WitnessTable *>(equatableTable));

// If these are equal, skip to the next element.
if (isEqual)
continue;

// Now that we know they are not equal, we can call their less than function
// and return the result.
auto lessThanWitness = comparableTable[1 + WitnessTableFirstRequirementOffset];
auto lessThan = reinterpret_cast<StaticInfixWitness *>(lessThanWitness);

// Call the less than function.
auto isLessThan = lessThan(value1, value2, elt.Type, elt.Type, conformance);

return isLessThan;
}

// Otherwise these tuples are completely equal, thus they are not less than
// each other.
return false;
}

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

// 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);

// Ensure we actually have a conformance to Comparable for this element type.
auto comparable = getComparableDescriptor();
auto conformance = conformsToProtocol(elt.Type, comparable);

// If we don't have a conformance then something somewhere messed up in
// deciding that this tuple type was Comparable...??
if (!conformance)
swift_unreachable("Tuple comparability requires that all elements \
be Comparable.");

// 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);

// First, grab the equatable conformance to prepare to check if the elements
// are equal. (Since this require Equatable conformance, the witness table
// is right after the conformance descriptor, which is at index 0.)
auto comparableTable = reinterpret_cast<void * const *>(conformance);
auto equatableTable = reinterpret_cast<void * const *>(comparableTable[1]);
auto equalsWitness = equatableTable[WitnessTableFirstRequirementOffset];
auto equals = reinterpret_cast<StaticInfixWitness *>(equalsWitness);

// Call the equal function.
auto isEqual = equals(value1, value2, elt.Type, elt.Type,
reinterpret_cast<const WitnessTable *>(equatableTable));

// If these are equal, skip to the next element.
if (isEqual)
continue;

// Now that we know they are not equal, we can call their less than or equal
// function and return the result.
auto lessThanOrEqualWitness =
comparableTable[WitnessTableFirstRequirementOffset + 2];
auto lessThanOrEqual =
reinterpret_cast<StaticInfixWitness *>(lessThanOrEqualWitness);

// Call the less than function.
auto isLessThanOrEqual = lessThanOrEqual(value1, value2, elt.Type, elt.Type,
conformance);

return isLessThanOrEqual;
}

// Otherwise these tuples are completely equal.
return true;
}

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

// 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);

// Ensure we actually have a conformance to Comparable for this element type.
auto comparable = getComparableDescriptor();
auto conformance = conformsToProtocol(elt.Type, comparable);

// If we don't have a conformance then something somewhere messed up in
// deciding that this tuple type was Comparable...??
if (!conformance)
swift_unreachable("Tuple comparability requires that all elements \
be Comparable.");

// 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);

// First, grab the equatable conformance to prepare to check if the elements
// are equal. (Since this require Equatable conformance, the witness table
// is right after the conformance descriptor, which is at index 0.)
auto comparableTable = reinterpret_cast<void * const *>(conformance);
auto equatableTable = reinterpret_cast<void * const *>(comparableTable[1]);
auto equalsWitness = equatableTable[WitnessTableFirstRequirementOffset];
auto equals = reinterpret_cast<StaticInfixWitness *>(equalsWitness);

// Call the equal function.
auto isEqual = equals(value1, value2, elt.Type, elt.Type,
reinterpret_cast<const WitnessTable *>(equatableTable));

// If these are equal, skip to the next element.
if (isEqual)
continue;

// Now that we know they are not equal, we can call their greater than or
// equal function and return the result.
auto greaterThanOrEqualWitness =
comparableTable[WitnessTableFirstRequirementOffset + 3];
auto greaterThanOrEqual =
reinterpret_cast<StaticInfixWitness *>(greaterThanOrEqualWitness);

// Call the greater than or equal function.
auto isGreaterThanOrEqual = greaterThanOrEqual(value1, value2, elt.Type,
elt.Type, conformance);

return isGreaterThanOrEqual;
}

// Otherwise these tuples are completely equal.
return true;
}

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

// 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);

// Ensure we actually have a conformance to Comparable for this element type.
auto comparable = getComparableDescriptor();
auto conformance = conformsToProtocol(elt.Type, comparable);

// If we don't have a conformance then something somewhere messed up in
// deciding that this tuple type was Comparable...??
if (!conformance)
swift_unreachable("Tuple comparability requires that all elements \
be Comparable.");

// 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);

// First, grab the equatable conformance to prepare to check if the elements
// are equal. (Since this require Equatable conformance, the witness table
// is right after the conformance descriptor, which is at index 0.)
auto comparableTable = reinterpret_cast<void * const *>(conformance);
auto equatableTable = reinterpret_cast<void * const *>(comparableTable[1]);
auto equalsWitness = equatableTable[WitnessTableFirstRequirementOffset];
auto equals = reinterpret_cast<StaticInfixWitness *>(equalsWitness);

// Call the equal function.
auto isEqual = equals(value1, value2, elt.Type, elt.Type,
reinterpret_cast<const WitnessTable *>(equatableTable));

// If these are equal, skip to the next element.
if (isEqual)
continue;

// Now that we know they are not equal, we can call their greater than
// function and return the result.
auto greaterThanWitness =
comparableTable[WitnessTableFirstRequirementOffset + 4];
auto greaterThan =
reinterpret_cast<StaticInfixWitness *>(greaterThanWitness);

// Call the greater than function.
auto isGreaterThan = greaterThan(value1, value2, elt.Type, elt.Type,
conformance);

return isGreaterThan;
}

// Otherwise these tuples are completely equal, thus they are not greater than
// each other.
return false;
}

0 comments on commit 13b5bf4

Please sign in to comment.