Skip to content

Commit 32f577a

Browse files
committed
[Runtime] Perform “deep” comparisons of witness tables when uniquing metadata.
Metadata uniquing might encounter witness tables that were distinctly generated but come from identical descriptors. Handle this case in metadata uniquing be looking into the protocol conformance descriptors themselves.
1 parent d9bb81b commit 32f577a

File tree

4 files changed

+116
-23
lines changed

4 files changed

+116
-23
lines changed

stdlib/public/runtime/Metadata.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -527,7 +527,7 @@ swift::swift_getGenericMetadata(MetadataRequest request,
527527
auto &generics = description->getFullGenericContextHeader();
528528
size_t numGenericArgs = generics.Base.NumKeyArguments;
529529

530-
auto key = MetadataCacheKey(arguments, numGenericArgs);
530+
auto key = MetadataCacheKey(description, arguments, numGenericArgs);
531531
auto result =
532532
getCache(generics).getOrInsert(key, request, description, arguments);
533533

@@ -4338,7 +4338,7 @@ static Result performOnMetadataCache(const Metadata *metadata,
43384338
reinterpret_cast<const void * const *>(
43394339
description->getGenericArguments(metadata));
43404340
size_t numGenericArgs = generics.Base.NumKeyArguments;
4341-
auto key = MetadataCacheKey(genericArgs, numGenericArgs);
4341+
auto key = MetadataCacheKey(description, genericArgs, numGenericArgs);
43424342

43434343
return std::move(callbacks).forGenericMetadata(metadata, description,
43444344
getCache(generics), key);

stdlib/public/runtime/MetadataCache.h

Lines changed: 108 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -353,26 +353,103 @@ class SimpleLockingCacheEntryBase {
353353

354354
/// A key value as provided to the concurrent map.
355355
class MetadataCacheKey {
356+
const TypeContextDescriptor *Description;
356357
const void * const *Data;
357358
uint32_t Length;
358359
uint32_t Hash;
359360

361+
/// Compare two witness tables, which may involving checking the
362+
/// contents of their conformance descriptors.
363+
static int compareWitnessTables(const WitnessTable *awt,
364+
const WitnessTable *bwt) {
365+
if (awt == bwt)
366+
return 0;
367+
368+
auto *aDescription = awt->Description;
369+
auto *bDescription = bwt->Description;
370+
if (aDescription == bDescription)
371+
return 0;
372+
373+
if (!aDescription->isSynthesizedNonUnique() ||
374+
!bDescription->isSynthesizedNonUnique())
375+
return comparePointers(aDescription, bDescription);
376+
377+
auto aType = aDescription->getCanonicalTypeMetadata();
378+
auto bType = bDescription->getCanonicalTypeMetadata();
379+
if (!aType || !bType)
380+
return comparePointers(aDescription, bDescription);
381+
382+
if (int result = comparePointers(aType, bType))
383+
return result;
384+
385+
return comparePointers(aDescription->getProtocol(),
386+
bDescription->getProtocol());
387+
}
388+
389+
/// Compare the content from two keys.
390+
static int compareContent(
391+
const TypeContextDescriptor *description,
392+
const void * const *adata,
393+
const void * const *bdata,
394+
unsigned size) {
395+
auto genericContext = description->getGenericContext();
396+
if (!genericContext) {
397+
assert(size == 0);
398+
return 0;
399+
}
400+
401+
// Compare generic parameters.
402+
for (const auto &gp : genericContext->getGenericParams()) {
403+
if (gp.hasKeyArgument()) {
404+
assert(size > 0);
405+
--size;
406+
if (auto result = comparePointers(*adata++, *bdata++))
407+
return result;
408+
}
409+
}
410+
411+
// Compare generic requirements.
412+
for (const auto &req : genericContext->getGenericRequirements()) {
413+
if (req.Flags.hasKeyArgument()) {
414+
assert(size > 0);
415+
--size;
416+
if (req.getKind() == GenericRequirementKind::Protocol) {
417+
if (auto result =
418+
compareWitnessTables((const WitnessTable *)*adata++,
419+
(const WitnessTable *)*bdata++))
420+
return result;
421+
} else {
422+
if (auto result = comparePointers(*adata++, *bdata++))
423+
return result;
424+
}
425+
}
426+
}
427+
428+
assert(size == 0);
429+
return 0;
430+
}
431+
360432
public:
361-
MetadataCacheKey(const void * const *data, size_t size)
362-
: Data(data), Length(size), Hash(computeHash()) {}
363-
MetadataCacheKey(const void * const *data, size_t size, uint32_t hash)
364-
: Data(data), Length(size), Hash(hash) {}
433+
MetadataCacheKey(const TypeContextDescriptor *description,
434+
const void * const *data, size_t size)
435+
: Description(description), Data(data), Length(size),
436+
Hash(computeHash()) {}
437+
MetadataCacheKey(const TypeContextDescriptor *description,
438+
const void * const *data, size_t size, uint32_t hash)
439+
: Description(description), Data(data), Length(size), Hash(hash) {}
365440

366441
bool operator==(MetadataCacheKey rhs) const {
442+
assert(Description == rhs.Description);
443+
444+
// Compare the hashes.
445+
if (hash() != rhs.hash()) return false;
446+
367447
// Compare the sizes.
368448
unsigned asize = size(), bsize = rhs.size();
369449
if (asize != bsize) return false;
370450

371451
// Compare the content.
372-
auto abegin = begin(), bbegin = rhs.begin();
373-
for (unsigned i = 0; i < asize; ++i)
374-
if (abegin[i] != bbegin[i]) return false;
375-
return true;
452+
return compareContent(Description, begin(), rhs.begin(), asize) == 0;
376453
}
377454

378455
int compare(const MetadataCacheKey &rhs) const {
@@ -387,16 +464,11 @@ class MetadataCacheKey {
387464
}
388465

389466
// Compare the content.
390-
auto lbegin = begin(), rbegin = rhs.begin();
391-
for (unsigned i = 0, e = size(); i != e; ++i) {
392-
if (auto ptrComparison = comparePointers(lbegin[i], rbegin[i]))
393-
return ptrComparison;
394-
}
395-
396-
// Equal.
397-
return 0;
467+
return compareContent(Description, begin(), rhs.begin(), size());
398468
}
399469

470+
const TypeContextDescriptor *description() const { return Description; }
471+
400472
uint32_t hash() const {
401473
return Hash;
402474
}
@@ -408,11 +480,23 @@ class MetadataCacheKey {
408480
private:
409481
uint32_t computeHash() const {
410482
size_t H = 0x56ba80d1 * Length;
411-
for (unsigned i = 0; i < Length; i++) {
483+
auto addValue = [&](const void *ptr) {
412484
H = (H >> 10) | (H << ((sizeof(size_t) * 8) - 10));
413-
H ^= (reinterpret_cast<size_t>(Data[i])
414-
^ (reinterpret_cast<size_t>(Data[i]) >> 19));
485+
H ^= (reinterpret_cast<size_t>(ptr)
486+
^ (reinterpret_cast<size_t>(ptr) >> 19));
487+
};
488+
489+
// Hash only the generic arguments; the witness tables need a deeper
490+
// comparison.
491+
if (auto genericContext = Description->getGenericContext()) {
492+
unsigned index = 0;
493+
for (const auto &gp : genericContext->getGenericParams()) {
494+
if (gp.hasKeyArgument()) {
495+
addValue(Data[index++]);
496+
}
497+
}
415498
}
499+
416500
H *= 0x27d4eb2d;
417501

418502
// Rotate right by 10 and then truncate to 32 bits.
@@ -1287,6 +1371,8 @@ class VariadicMetadataCacheEntryBase :
12871371
const uint16_t KeyLength;
12881372
const uint32_t Hash;
12891373

1374+
const TypeContextDescriptor *Description;
1375+
12901376
/// Valid if TrackingInfo.getState() >= PrivateMetadataState::Abstract.
12911377
ValueType Value;
12921378

@@ -1300,13 +1386,14 @@ class VariadicMetadataCacheEntryBase :
13001386

13011387
public:
13021388
VariadicMetadataCacheEntryBase(const MetadataCacheKey &key)
1303-
: KeyLength(key.size()), Hash(key.hash()) {
1389+
: KeyLength(key.size()), Hash(key.hash()), Description(key.description()) {
13041390
memcpy(this->template getTrailingObjects<const void *>(),
13051391
key.begin(), key.size() * sizeof(const void *));
13061392
}
13071393

13081394
MetadataCacheKey getKey() const {
1309-
return MetadataCacheKey(this->template getTrailingObjects<const void*>(),
1395+
return MetadataCacheKey(Description,
1396+
this->template getTrailingObjects<const void*>(),
13101397
KeyLength, Hash);
13111398
}
13121399

test/Runtime/Inputs/synthesized_decl_uniqueness.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import CoreLocation
2+
import Foundation
23

34
public func getCLError() -> Any.Type {
45
return CLError.self
@@ -7,3 +8,7 @@ public func getCLError() -> Any.Type {
78
public func getCLErrorCode() -> Any.Type {
89
return CLError.Code.self
910
}
11+
12+
public func getNotificationNameSet() -> Any.Type {
13+
return Set<NSNotification.Name>.self
14+
}

test/Runtime/synthesized_decl_uniqueness.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ var tests = TestSuite("metadata identity for synthesized types")
1717
tests.test("synthesized type identity across modules") {
1818
expectEqual(A.getCLError(), B.getCLError())
1919
expectEqual(A.getCLErrorCode(), B.getCLErrorCode())
20+
expectEqual(A.getNotificationNameSet(), B.getNotificationNameSet())
2021
}
2122

2223
runAllTests()

0 commit comments

Comments
 (0)