2828#include < mach-o/dyld_priv.h>
2929#endif
3030
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+
3136using namespace swift ;
3237
3338static std::atomic<bool > disablePrespecializedMetadata = false ;
@@ -171,12 +176,20 @@ struct LibPrespecializedState {
171176 struct AddressRange {
172177 uintptr_t start, end;
173178
174- bool contains (const void *ptr) {
179+ bool contains (const void *ptr) const {
175180 return start <= (uintptr_t )ptr && (uintptr_t )ptr < end;
176181 }
177182 };
178183
184+ enum class MapConfiguration {
185+ UseNameKeyedMap,
186+ UsePointerKeyedMap,
187+ UsePointerKeyedMapDebugMode,
188+ Disabled,
189+ };
190+
179191 const LibPrespecializedData<InProcess> *data;
192+ MapConfiguration mapConfiguration;
180193 AddressRange sharedCacheRange{0 , 0 };
181194 AddressRange metadataAllocatorInitialPoolRange{0 , 0 };
182195
@@ -197,6 +210,60 @@ struct LibPrespecializedState {
197210 metadataAllocatorInitialPoolRange.end =
198211 metadataAllocatorInitialPoolRange.start + initialPoolLength;
199212#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;
200267 }
201268};
202269
@@ -218,7 +285,7 @@ hasNonTypeGenericArguments(const TargetGenericContext<InProcess> *generics) {
218285}
219286
220287static bool
221- isPotentialPrespecializedPointer (LibPrespecializedState &prespecialized ,
288+ isPotentialPrespecializedPointer (const LibPrespecializedState &state ,
222289 const void *pointer) {
223290 // Prespecialized metadata descriptors and arguments are always in the shared
224291 // cache. They're either statically emitted metadata, or they're
@@ -228,16 +295,16 @@ isPotentialPrespecializedPointer(LibPrespecializedState &prespecialized,
228295 // If we're loading a debug libprespecialized, we can't do these checks, so
229296 // just say everything is a potential argument. Performance is not so
230297 // important in that case.
231- if (!prespecialized .sharedCacheRange .contains (prespecialized .data ))
298+ if (!state .sharedCacheRange .contains (state .data ))
232299 return true ;
233300
234301 // Anything outside the shared cache isn't a potential argument.
235- if (!prespecialized .sharedCacheRange .contains (pointer))
302+ if (!state .sharedCacheRange .contains (pointer))
236303 return false ;
237304
238305 // Dynamically allocated metadata could be within the shared cache, in the
239306 // initial metadata allocation pool. Reject anything in that region.
240- if (prespecialized .metadataAllocatorInitialPoolRange .contains (pointer))
307+ if (state .metadataAllocatorInitialPoolRange .contains (pointer))
241308 return false ;
242309
243310 return true ;
@@ -256,20 +323,10 @@ swift::libPrespecializedImageLoaded() {
256323 #endif
257324}
258325
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) {
273330 auto *generics = description->getGenericContext ();
274331 if (!generics)
275332 return nullptr ;
@@ -279,15 +336,15 @@ swift::getLibPrespecializedMetadata(const TypeContextDescriptor *description,
279336 if (hasNonTypeGenericArguments (generics))
280337 return nullptr ;
281338
282- if (!isPotentialPrespecializedPointer (prespecialized , description)) {
339+ if (!isPotentialPrespecializedPointer (state , description)) {
283340 LOG (" Rejecting descriptor %p, not in the shared cache" ,
284341 (const void *)description);
285342 return nullptr ;
286343 }
287344
288345 auto numKeyArguments = generics->getGenericContextHeader ().NumKeyArguments ;
289346 for (unsigned i = 0 ; i < numKeyArguments; i++) {
290- if (!isPotentialPrespecializedPointer (prespecialized , arguments[i])) {
347+ if (!isPotentialPrespecializedPointer (state , arguments[i])) {
291348 LOG (" Rejecting argument %u %p to descriptor %p, not in the shared cache" ,
292349 i, arguments[i], (const void *)description);
293350 return nullptr ;
@@ -322,13 +379,120 @@ swift::getLibPrespecializedMetadata(const TypeContextDescriptor *description,
322379 }
323380
324381 auto key = mangling.result ();
325- auto *metadataMap = data->getMetadataMap ();
382+ auto *metadataMap = state. data ->getMetadataMap ();
326383 auto *element = metadataMap->find (key.data (), key.size ());
327384 auto *result = element ? element->value : nullptr ;
328385 LOG (" found %p for key '%.*s'." , result, (int )key.size (), key.data ());
329386 return result;
330387}
331388
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+
332496void _swift_validatePrespecializedMetadata () {
333497 auto *data = getLibPrespecializedData ();
334498 if (!data) {
0 commit comments