28
28
#include < mach-o/dyld_priv.h>
29
29
#endif
30
30
31
+ #if __has_include(<os/feature_private.h>)
32
+ #include < os/feature_private.h> // for os_feature_enabled_simple()
33
+ #define HAS_OS_FEATURE 1
34
+ #endif
35
+
31
36
using namespace swift ;
32
37
33
38
static std::atomic<bool > disablePrespecializedMetadata = false ;
@@ -171,12 +176,20 @@ struct LibPrespecializedState {
171
176
struct AddressRange {
172
177
uintptr_t start, end;
173
178
174
- bool contains (const void *ptr) {
179
+ bool contains (const void *ptr) const {
175
180
return start <= (uintptr_t )ptr && (uintptr_t )ptr < end;
176
181
}
177
182
};
178
183
184
+ enum class MapConfiguration {
185
+ UseNameKeyedMap,
186
+ UsePointerKeyedMap,
187
+ UsePointerKeyedMapDebugMode,
188
+ Disabled,
189
+ };
190
+
179
191
const LibPrespecializedData<InProcess> *data;
192
+ MapConfiguration mapConfiguration;
180
193
AddressRange sharedCacheRange{0 , 0 };
181
194
AddressRange metadataAllocatorInitialPoolRange{0 , 0 };
182
195
@@ -197,6 +210,60 @@ struct LibPrespecializedState {
197
210
metadataAllocatorInitialPoolRange.end =
198
211
metadataAllocatorInitialPoolRange.start + initialPoolLength;
199
212
#endif
213
+
214
+ // Must do this after the shared cache range has been retrieved.
215
+ mapConfiguration = computeMapConfiguration (data);
216
+ }
217
+
218
+ MapConfiguration
219
+ computeMapConfiguration (const LibPrespecializedData<InProcess> *data) {
220
+ // If no data, we have to disable.
221
+ if (!data)
222
+ return MapConfiguration::Disabled;
223
+
224
+ auto nameKeyedMap = data->getMetadataMap ();
225
+ auto pointerKeyedMap = data->getPointerKeyedMetadataMap ();
226
+
227
+ // If we don't have either map, then disable it completely.
228
+ if (!nameKeyedMap && !pointerKeyedMap) {
229
+ LOG (" No prespecializations map available from data at %p, disabling." ,
230
+ data);
231
+ return MapConfiguration::Disabled;
232
+ }
233
+
234
+ // If we don't have the pointer-keyed map, fall back to the name-keyed map.
235
+ if (!pointerKeyedMap) {
236
+ LOG (" Data at %p only contains name-keyed map." , data);
237
+ return MapConfiguration::UseNameKeyedMap;
238
+ }
239
+
240
+ // If we don't have the name-keyed map, always use the pointer-keyed map.
241
+ if (!nameKeyedMap) {
242
+ LOG (" Data at %p only contains pointer-keyed map." , data);
243
+ return MapConfiguration::UsePointerKeyedMap;
244
+ }
245
+
246
+ // We have both. Consult the option flag.
247
+ bool usePointerKeyedMap =
248
+ data->getOptionFlags () &
249
+ LibPrespecializedData<InProcess>::OptionFlagDefaultToPointerKeyedMap;
250
+
251
+ #if HAS_OS_FEATURE
252
+ if (os_feature_enabled_simple (Swift, useAlternatePrespecializationMap,
253
+ false ))
254
+ usePointerKeyedMap = !usePointerKeyedMap;
255
+ #endif
256
+
257
+ LOG (" Data at %p contains both maps. Using %s keyed map." , data,
258
+ usePointerKeyedMap ? " pointer" : " name" );
259
+ if (usePointerKeyedMap) {
260
+ // If we're using a map outside the shared cache, then we're in debug mode
261
+ // and need to use our own slow lookup.
262
+ if (!sharedCacheRange.contains (pointerKeyedMap))
263
+ return MapConfiguration::UsePointerKeyedMapDebugMode;
264
+ return MapConfiguration::UsePointerKeyedMap;
265
+ }
266
+ return MapConfiguration::UseNameKeyedMap;
200
267
}
201
268
};
202
269
@@ -218,7 +285,7 @@ hasNonTypeGenericArguments(const TargetGenericContext<InProcess> *generics) {
218
285
}
219
286
220
287
static bool
221
- isPotentialPrespecializedPointer (LibPrespecializedState &prespecialized ,
288
+ isPotentialPrespecializedPointer (const LibPrespecializedState &state ,
222
289
const void *pointer) {
223
290
// Prespecialized metadata descriptors and arguments are always in the shared
224
291
// cache. They're either statically emitted metadata, or they're
@@ -228,16 +295,16 @@ isPotentialPrespecializedPointer(LibPrespecializedState &prespecialized,
228
295
// If we're loading a debug libprespecialized, we can't do these checks, so
229
296
// just say everything is a potential argument. Performance is not so
230
297
// important in that case.
231
- if (!prespecialized .sharedCacheRange .contains (prespecialized .data ))
298
+ if (!state .sharedCacheRange .contains (state .data ))
232
299
return true ;
233
300
234
301
// Anything outside the shared cache isn't a potential argument.
235
- if (!prespecialized .sharedCacheRange .contains (pointer))
302
+ if (!state .sharedCacheRange .contains (pointer))
236
303
return false ;
237
304
238
305
// Dynamically allocated metadata could be within the shared cache, in the
239
306
// initial metadata allocation pool. Reject anything in that region.
240
- if (prespecialized .metadataAllocatorInitialPoolRange .contains (pointer))
307
+ if (state .metadataAllocatorInitialPoolRange .contains (pointer))
241
308
return false ;
242
309
243
310
return true ;
@@ -256,20 +323,10 @@ swift::libPrespecializedImageLoaded() {
256
323
#endif
257
324
}
258
325
259
- Metadata *
260
- swift::getLibPrespecializedMetadata (const TypeContextDescriptor *description,
261
- const void *const *arguments) {
262
- if (SWIFT_UNLIKELY (
263
- disableForValidation
264
- || disablePrespecializedMetadata.load (std::memory_order_acquire)))
265
- return nullptr ;
266
-
267
- auto &prespecialized = LibPrespecialized.get ();
268
-
269
- auto *data = prespecialized.data ;
270
- if (!data)
271
- return nullptr ;
272
-
326
+ static Metadata *
327
+ getMetadataFromNameKeyedMap (const LibPrespecializedState &state,
328
+ const TypeContextDescriptor *description,
329
+ const void *const *arguments) {
273
330
auto *generics = description->getGenericContext ();
274
331
if (!generics)
275
332
return nullptr ;
@@ -279,15 +336,15 @@ swift::getLibPrespecializedMetadata(const TypeContextDescriptor *description,
279
336
if (hasNonTypeGenericArguments (generics))
280
337
return nullptr ;
281
338
282
- if (!isPotentialPrespecializedPointer (prespecialized , description)) {
339
+ if (!isPotentialPrespecializedPointer (state , description)) {
283
340
LOG (" Rejecting descriptor %p, not in the shared cache" ,
284
341
(const void *)description);
285
342
return nullptr ;
286
343
}
287
344
288
345
auto numKeyArguments = generics->getGenericContextHeader ().NumKeyArguments ;
289
346
for (unsigned i = 0 ; i < numKeyArguments; i++) {
290
- if (!isPotentialPrespecializedPointer (prespecialized , arguments[i])) {
347
+ if (!isPotentialPrespecializedPointer (state , arguments[i])) {
291
348
LOG (" Rejecting argument %u %p to descriptor %p, not in the shared cache" ,
292
349
i, arguments[i], (const void *)description);
293
350
return nullptr ;
@@ -322,13 +379,120 @@ swift::getLibPrespecializedMetadata(const TypeContextDescriptor *description,
322
379
}
323
380
324
381
auto key = mangling.result ();
325
- auto *metadataMap = data->getMetadataMap ();
382
+ auto *metadataMap = state. data ->getMetadataMap ();
326
383
auto *element = metadataMap->find (key.data (), key.size ());
327
384
auto *result = element ? element->value : nullptr ;
328
385
LOG (" found %p for key '%.*s'." , result, (int )key.size (), key.data ());
329
386
return result;
330
387
}
331
388
389
+ static Metadata *
390
+ getMetadataFromPointerKeyedMap (const LibPrespecializedState &state,
391
+ const TypeContextDescriptor *description,
392
+ const void *const *arguments) {
393
+ #if DYLD_FIND_POINTER_HASH_TABLE_ENTRY_DEFINED
394
+ auto *generics = description->getGenericContext ();
395
+ if (!generics)
396
+ return nullptr ;
397
+
398
+ auto argumentCount = generics->getGenericContextHeader ().NumKeyArguments ;
399
+
400
+ auto *map = state.data ->getPointerKeyedMetadataMap ();
401
+ auto result = _dyld_find_pointer_hash_table_entry (
402
+ map, description, argumentCount, const_cast <const void **>(arguments));
403
+ LOG (" Looking up description %p in dyld table, found %p." , description,
404
+ result);
405
+ return reinterpret_cast <Metadata *>(const_cast <void *>(result));
406
+ #else
407
+ LOG (" Looking up description %p but dyld hash table call not available." ,
408
+ description);
409
+ return nullptr ;
410
+ #endif
411
+ }
412
+
413
+ // When we have a pointer-keyed map from a debug library, it's not built as a
414
+ // hash table. We just scan it linearly.
415
+ static Metadata *getMetadataFromPointerKeyedMapDebugMode (
416
+ const LibPrespecializedState &state,
417
+ const TypeContextDescriptor *description, const void *const *arguments) {
418
+ auto *generics = description->getGenericContext ();
419
+ if (!generics)
420
+ return nullptr ;
421
+
422
+ auto argumentCount = generics->getGenericContextHeader ().NumKeyArguments ;
423
+ auto *mapPtr = state.data ->getPointerKeyedMetadataMap ();
424
+
425
+ struct MapKey {
426
+ size_t count;
427
+ void *pointers[];
428
+ };
429
+
430
+ struct MapEntry {
431
+ const MapKey *key;
432
+ Metadata *value;
433
+ };
434
+
435
+ struct Map {
436
+ size_t count;
437
+ MapEntry entries[];
438
+ };
439
+
440
+ const Map *map = reinterpret_cast <const Map *>(mapPtr);
441
+ for (size_t i = 0 ; i < map->count ; i++) {
442
+ auto &entry = map->entries [i];
443
+
444
+ // Keys are descriptor followed by arguments, so their count is 1 plus the
445
+ // argument count.
446
+ if (entry.key ->count != argumentCount + 1 )
447
+ continue ;
448
+
449
+ // Check the descriptor.
450
+ if (description != entry.key ->pointers [0 ])
451
+ continue ;
452
+
453
+ // Check the rest. The pointers array is now offset by 1 since index 0 is
454
+ // the descriptor.
455
+ bool equal = true ;
456
+ for (size_t j = 0 ; j < argumentCount; j++) {
457
+ if (entry.key ->pointers [j + 1 ] != arguments[j]) {
458
+ equal = false ;
459
+ break ;
460
+ }
461
+ }
462
+
463
+ if (equal) {
464
+ LOG (" Looking up description %p in debug table, found %p." , description,
465
+ entry.value );
466
+ return entry.value ;
467
+ }
468
+ }
469
+
470
+ LOG (" Looking up description %p in debug table, no entry found." , description);
471
+ return nullptr ;
472
+ }
473
+
474
+ Metadata *
475
+ swift::getLibPrespecializedMetadata (const TypeContextDescriptor *description,
476
+ const void *const *arguments) {
477
+ if (SWIFT_UNLIKELY (disableForValidation || disablePrespecializedMetadata.load (
478
+ std::memory_order_acquire)))
479
+ return nullptr ;
480
+
481
+ auto &state = LibPrespecialized.get ();
482
+
483
+ switch (state.mapConfiguration ) {
484
+ case LibPrespecializedState::MapConfiguration::Disabled:
485
+ return nullptr ;
486
+ case LibPrespecializedState::MapConfiguration::UseNameKeyedMap:
487
+ return getMetadataFromNameKeyedMap (state, description, arguments);
488
+ case LibPrespecializedState::MapConfiguration::UsePointerKeyedMap:
489
+ return getMetadataFromPointerKeyedMap (state, description, arguments);
490
+ case LibPrespecializedState::MapConfiguration::UsePointerKeyedMapDebugMode:
491
+ return getMetadataFromPointerKeyedMapDebugMode (state, description,
492
+ arguments);
493
+ }
494
+ }
495
+
332
496
void _swift_validatePrespecializedMetadata () {
333
497
auto *data = getLibPrespecializedData ();
334
498
if (!data) {
0 commit comments