Skip to content

Commit 98229fe

Browse files
committed
[Runtime] Don't attempt to look up prespecialized metadata involving pointers outside the shared cache.
The descriptor and arguments for prespecialized metadata will always be in the shared cache. Skip creating the mangling for any lookup involving pointers outside the shared cache, as an optimization.
1 parent ac1ce06 commit 98229fe

File tree

3 files changed

+109
-17
lines changed

3 files changed

+109
-17
lines changed

stdlib/public/runtime/LibPrespecialized.cpp

Lines changed: 96 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
//===----------------------------------------------------------------------===//
1212

1313
#include "swift/Runtime/LibPrespecialized.h"
14+
#include "MetadataCache.h"
1415
#include "Private.h"
1516
#include "swift/Basic/Lazy.h"
1617
#include "swift/Runtime/EnvironmentVariables.h"
@@ -71,24 +72,89 @@ static const LibPrespecializedData<InProcess> *findLibPrespecialized() {
7172
return data;
7273
}
7374

75+
struct LibPrespecializedState {
76+
struct AddressRange {
77+
uintptr_t start, end;
78+
79+
bool contains(const void *ptr) {
80+
return start <= (uintptr_t)ptr && (uintptr_t)ptr < end;
81+
}
82+
};
83+
84+
bool loggingEnabled;
85+
const LibPrespecializedData<InProcess> *data;
86+
AddressRange sharedCacheRange{0, 0};
87+
AddressRange metadataAllocatorInitialPoolRange{0, 0};
88+
89+
LibPrespecializedState() {
90+
loggingEnabled =
91+
runtime::environment::SWIFT_DEBUG_ENABLE_LIB_PRESPECIALIZED_LOGGING();
92+
data = findLibPrespecialized();
93+
94+
#if DYLD_GET_SWIFT_PRESPECIALIZED_DATA_DEFINED
95+
size_t sharedCacheLength;
96+
sharedCacheRange.start =
97+
(uintptr_t)_dyld_get_shared_cache_range(&sharedCacheLength);
98+
sharedCacheRange.end = sharedCacheRange.start + sharedCacheLength;
99+
100+
auto [initialPoolStart, initialPoolLength] =
101+
MetadataAllocator::InitialPoolLocation();
102+
metadataAllocatorInitialPoolRange.start = (uintptr_t)initialPoolStart;
103+
metadataAllocatorInitialPoolRange.end =
104+
metadataAllocatorInitialPoolRange.start + initialPoolLength;
105+
#endif
106+
}
107+
};
108+
109+
static Lazy<LibPrespecializedState> LibPrespecialized;
110+
111+
#define LOG(fmt, ...) \
112+
do { \
113+
if (SWIFT_UNLIKELY(prespecialized.loggingEnabled)) \
114+
fprintf(stderr, "Prespecializations library: " fmt "\n", __VA_ARGS__); \
115+
} while (0)
116+
74117
const LibPrespecializedData<InProcess> *swift::getLibPrespecializedData() {
75118
return SWIFT_LAZY_CONSTANT(findLibPrespecialized());
76119
}
77120

78121
// Returns true if the type has any arguments that aren't plain types (packs or
79122
// unknown kinds).
80-
static bool hasNonTypeGenericArguments(const TypeContextDescriptor *description) {
81-
auto generics = description->getGenericContext();
82-
if (!generics)
83-
return false;
84-
123+
static bool
124+
hasNonTypeGenericArguments(const TargetGenericContext<InProcess> *generics) {
85125
for (auto param : generics->getGenericParams())
86126
if (param.getKind() != GenericParamKind::Type)
87127
return true;
88128

89129
return false;
90130
}
91131

132+
static bool
133+
isPotentialPrespecializedPointer(LibPrespecializedState &prespecialized,
134+
const void *pointer) {
135+
// Prespecialized metadata descriptors and arguments are always in the shared
136+
// cache. They're either statically emitted metadata, or they're
137+
// prespecialized metadata. Anything that's dynamically allocated, or
138+
// statically allocated outside the shared cache, is not a possible candidate.
139+
140+
// If we're loading a debug libprespecialized, we can't do these checks, so
141+
// just say everything is a potential argument. Performance is not so
142+
// important in that case.
143+
if (!prespecialized.sharedCacheRange.contains(prespecialized.data))
144+
return true;
145+
146+
// Anything outside the shared cache isn't a potential argument.
147+
if (!prespecialized.sharedCacheRange.contains(pointer))
148+
return false;
149+
150+
// Dynamically allocated metadata could be within the shared cache, in the
151+
// initial metadata allocation pool. Reject anything in that region.
152+
if (prespecialized.metadataAllocatorInitialPoolRange.contains(pointer))
153+
return false;
154+
155+
return true;
156+
}
157+
92158
static bool disableForValidation = false;
93159

94160
Metadata *
@@ -97,24 +163,40 @@ swift::getLibPrespecializedMetadata(const TypeContextDescriptor *description,
97163
if (disableForValidation)
98164
return nullptr;
99165

100-
auto *data = getLibPrespecializedData();
166+
auto &prespecialized = LibPrespecialized.get();
167+
168+
auto *data = prespecialized.data;
101169
if (!data)
102170
return nullptr;
103171

172+
auto *generics = description->getGenericContext();
173+
if (!generics)
174+
return nullptr;
175+
104176
// We don't support types with pack parameters yet (and especially not types
105177
// with unknown parameter kinds) so don't even try to look those up.
106-
if (hasNonTypeGenericArguments(description))
178+
if (hasNonTypeGenericArguments(generics))
107179
return nullptr;
108180

181+
if (!isPotentialPrespecializedPointer(prespecialized, description)) {
182+
LOG("Rejecting descriptor %p, not in the shared cache",
183+
(const void *)description);
184+
return nullptr;
185+
}
186+
187+
auto numKeyArguments = generics->getGenericContextHeader().NumKeyArguments;
188+
for (unsigned i = 0; i < numKeyArguments; i++) {
189+
if (!isPotentialPrespecializedPointer(prespecialized, arguments[i])) {
190+
LOG("Rejecting argument %u %p to descriptor %p, not in the shared cache",
191+
i, arguments[i], (const void *)description);
192+
return nullptr;
193+
}
194+
}
195+
109196
Demangler dem;
110197
auto mangleNode = _buildDemanglingForGenericType(description, arguments, dem);
111198
if (!mangleNode) {
112-
if (SWIFT_UNLIKELY(runtime::environment::
113-
SWIFT_DEBUG_ENABLE_LIB_PRESPECIALIZED_LOGGING()))
114-
fprintf(stderr,
115-
"Prespecializations library: failed to build demangling with "
116-
"descriptor %p.\n",
117-
description);
199+
LOG("failed to build demangling with descriptor %p.", description);
118200
return nullptr;
119201
}
120202

@@ -142,10 +224,7 @@ swift::getLibPrespecializedMetadata(const TypeContextDescriptor *description,
142224
auto *metadataMap = data->getMetadataMap();
143225
auto *element = metadataMap->find(key.data(), key.size());
144226
auto *result = element ? element->value : nullptr;
145-
if (SWIFT_UNLIKELY(runtime::environment::
146-
SWIFT_DEBUG_ENABLE_LIB_PRESPECIALIZED_LOGGING()))
147-
fprintf(stderr, "Prespecializations library: found %p for key '%.*s'.\n",
148-
result, (int)key.size(), key.data());
227+
LOG("found %p for key '%.*s'.", result, (int)key.size(), key.data());
149228
return result;
150229
}
151230

stdlib/public/runtime/Metadata.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7420,6 +7420,10 @@ static swift::atomic<PoolRange>
74207420
AllocationPool{PoolRange{InitialAllocationPool.Pool,
74217421
sizeof(InitialAllocationPool.Pool)}};
74227422

7423+
std::tuple<const void *, size_t> MetadataAllocator::InitialPoolLocation() {
7424+
return {InitialAllocationPool.Pool, sizeof(InitialAllocationPool.Pool)};
7425+
}
7426+
74237427
bool swift::_swift_debug_metadataAllocationIterationEnabled = false;
74247428
const void * const swift::_swift_debug_allocationPoolPointer = &AllocationPool;
74257429
std::atomic<const void *> swift::_swift_debug_metadataAllocationBacktraceList;

stdlib/public/runtime/MetadataCache.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include <atomic>
2626
#include <condition_variable>
2727
#include <optional>
28+
#include <tuple>
2829

2930
#ifndef SWIFT_DEBUG_RUNTIME
3031
#define SWIFT_DEBUG_RUNTIME 0
@@ -46,6 +47,11 @@ class MetadataAllocator : public llvm::AllocatorBase<MetadataAllocator> {
4647

4748
void Reset() {}
4849

50+
/// Get the location of the allocator's initial statically allocated pool.
51+
/// The return values are start and size. If there is no statically allocated
52+
/// pool, the return values are NULL, 0.
53+
static std::tuple<const void *, size_t> InitialPoolLocation();
54+
4955
SWIFT_RETURNS_NONNULL SWIFT_NODISCARD
5056
void *Allocate(size_t size, size_t alignment);
5157
using AllocatorBase<MetadataAllocator>::Allocate;
@@ -67,6 +73,9 @@ class MetadataAllocator : public llvm::AllocatorBase<MetadataAllocator> {
6773
class MetadataAllocator {
6874
public:
6975
MetadataAllocator(uint16_t tag) {}
76+
static std::tuple<const void *, size_t> InitialPoolLocation() {
77+
return {nullptr, 0};
78+
}
7079
SWIFT_RETURNS_NONNULL SWIFT_NODISCARD
7180
void *Allocate(size_t size, size_t alignment) {
7281
if (alignment < sizeof(void*)) alignment = sizeof(void*);

0 commit comments

Comments
 (0)