Skip to content

Commit b5d5421

Browse files
committed
[Runtime] Support type descriptor map in LibPrespecialized.
The descriptor map is keyed by a simplified mangling that canonicalizes the differences that we accept in _contextDescriptorMatchesMangling, such as the ability to specify any kind of type with an OtherNominalType node. This simplified mangling is not necessarily unique, but we use _contextDescriptorMatchesMangling for the final equality checking when looking up entries in the map, so occasional collisions are acceptable and get resolved when probing the table. The table is meant to be comprehensive, so it includes all descriptors that can be looked up by name, and a negative result means the descriptor does not exist in the shared cache. We add a flag to the options that can mark it as non-definitive in case we ever need to degrade this, and fall back to a full search after a negative result. The map encompasses the entire shared cache but we need to reject lookups for types in images that aren't loaded. The map includes an image index which allows us to cheaply query whether a given descriptor is in a loaded image or not, so we can ignore ones which are not. TypeMetadataPrivateState now has a separate sections array for sections within the shared cache. _searchTypeMetadataRecords consults the map first. If no result is found in the map and the map is marked as comprehensive, then only the sections outside the shared cache need to be scanned. Replace the SWIFT_DEBUG_ENABLE_LIB_PRESPECIALIZED environment variable with one specifically for metadata and one for descriptor lookup so they can be controlled independently. Also add SWIFT_DEBUG_VALIDATE_LIB_PRESPECIALIZED_DESCRIPTOR_LOOKUP which consults the map and does the full scan, and ensures they produce the same result, for debugging purposes. Remove the disablePrespecializedMetadata global and instead modify the mapConfiguration to disable prespecialized metadata when an image is loaded that overrides one in the shared cache. rdar://113059233
1 parent 9f2ee07 commit b5d5421

File tree

6 files changed

+710
-156
lines changed

6 files changed

+710
-156
lines changed

include/swift/Runtime/LibPrespecialized.h

Lines changed: 142 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "PrebuiltStringMap.h"
2121
#include "swift/ABI/Metadata.h"
2222
#include "swift/ABI/TargetLayout.h"
23+
#include "swift/Demangling/Demangler.h"
2324

2425
#define LIB_PRESPECIALIZED_TOP_LEVEL_SYMBOL_NAME "_swift_prespecializationsData"
2526

@@ -36,23 +37,40 @@ struct LibPrespecializedData {
3637

3738
typename Runtime::StoredSize optionFlags;
3839

40+
TargetPointer<Runtime, const void> descriptorMap;
41+
3942
// Existing fields are above, add new fields below this point.
4043

44+
// The major/minor version numbers for this version of the struct.
4145
static constexpr uint32_t currentMajorVersion = 1;
42-
static constexpr uint32_t currentMinorVersion = 3;
46+
static constexpr uint32_t currentMinorVersion = 4;
4347

48+
// Version numbers where various fields were introduced.
4449
static constexpr uint32_t minorVersionWithDisabledProcessesTable = 2;
4550
static constexpr uint32_t minorVersionWithPointerKeyedMetadataMap = 3;
4651
static constexpr uint32_t minorVersionWithOptionFlags = 3;
52+
static constexpr uint32_t minorVersionWithDescriptorMap = 4;
4753

4854
// Option flags values.
4955
enum : typename Runtime::StoredSize {
5056
// When this flag is set, the runtime should default to using the
5157
// pointer-keyed table. When not set, default to using the name-keyed table.
5258
OptionFlagDefaultToPointerKeyedMap = 1ULL << 0,
59+
60+
// When this flag is set, descriptorMap is not comprehensive, meaning that
61+
// a negative lookup result is not a definitive failure.
62+
OptionFlagDescriptorMapNotComprehensive = 1ULL << 1,
5363
};
5464

55-
// Helpers for retrieving the metadata map in-process.
65+
// Helpers for safely retrieving various fields. Helpers return 0 or NULL if
66+
// the version number indicates that the field is not present.
67+
68+
typename Runtime::StoredSize getOptionFlags() const {
69+
if (minorVersion < minorVersionWithOptionFlags)
70+
return 0;
71+
return optionFlags;
72+
}
73+
5674
static bool stringIsNull(const char *str) { return str == nullptr; }
5775

5876
using MetadataMap = PrebuiltStringMap<const char *, Metadata *, stringIsNull>;
@@ -73,18 +91,136 @@ struct LibPrespecializedData {
7391
return pointerKeyedMetadataMap;
7492
}
7593

76-
typename Runtime::StoredSize getOptionFlags() const {
77-
if (minorVersion < minorVersionWithOptionFlags)
78-
return 0;
79-
return optionFlags;
94+
using DescriptorMap =
95+
PrebuiltAuxDataImplicitStringMap<TargetPointer<Runtime, const void>,
96+
uint16_t>;
97+
98+
const DescriptorMap *getDescriptorMap() const {
99+
if (minorVersion < minorVersionWithDescriptorMap)
100+
return nullptr;
101+
return reinterpret_cast<const DescriptorMap *>(descriptorMap);
80102
}
81103
};
82104

105+
enum class LibPrespecializedLookupResult {
106+
// We found something.
107+
Found,
108+
109+
// We didn't find anything, and we know it's not in the shared cache.
110+
DefinitiveNotFound,
111+
112+
// We didn't find anything, but we couldn't rule out the shared cache. Caller
113+
// must do a full search.
114+
NonDefinitiveNotFound,
115+
};
116+
83117
const LibPrespecializedData<InProcess> *getLibPrespecializedData();
118+
84119
Metadata *getLibPrespecializedMetadata(const TypeContextDescriptor *description,
85120
const void *const *arguments);
86121
void libPrespecializedImageLoaded();
87122

123+
std::pair<LibPrespecializedLookupResult, const TypeContextDescriptor *>
124+
getLibPrespecializedTypeDescriptor(Demangle::NodePointer node);
125+
126+
/// Given the demangling referring to a particular descriptor, build the
127+
/// canonical simplified version of the demangling that's used for the keys in
128+
/// the descriptorMap. We copy across Extension and Module nodes. Type nodes are
129+
/// all normalized to be OtherNominalType to allow for the runtime allowing
130+
/// type kind mismatches on imported C types in certain cases. Other nodes are
131+
/// skipped.
132+
///
133+
/// The runtime always searches through duplicates in the table, and uses its
134+
/// own matching on all candidates, so the simplified demangling is allowed to
135+
/// be simplified to the point of having different descriptors sometimes produce
136+
/// the same demangling.
137+
static inline Demangle::NodePointer
138+
buildSimplifiedDescriptorDemangling(Demangle::NodePointer node,
139+
Demangle::Demangler &dem) {
140+
// The node that will be returned to the caller.
141+
Demangle::NodePointer result = nullptr;
142+
143+
// The bottommost node in the result that we've generated. Additional nodes
144+
// are added as children to this one.
145+
Demangle::NodePointer resultBottom = nullptr;
146+
147+
// The current node that we're iterating over in the input node tree.
148+
Demangle::NodePointer current = node;
149+
150+
using Kind = Demangle::Node::Kind;
151+
152+
// Helper to add a new node to the result. This sets `result` to the node if
153+
// it hasn't already been set (indicating this is the topmost node), and adds
154+
// the node as a child to `resultBottom` otherwise. `resultBottom` is updated
155+
// to point to the new node.
156+
auto addNode = [&](Demangle::NodePointer newNode) {
157+
if (!result) {
158+
result = newNode;
159+
} else {
160+
if (resultBottom->getKind() == Kind::Extension) {
161+
resultBottom->addChild(newNode, dem);
162+
} else {
163+
// Shift the Identifier down, insert before it.
164+
resultBottom->addChild(resultBottom->getFirstChild(), dem);
165+
resultBottom->replaceChild(0, newNode);
166+
}
167+
}
168+
resultBottom = newNode;
169+
};
170+
171+
// Walk down the input node tree.
172+
while (current) {
173+
switch (current->getKind()) {
174+
case Kind::Extension: {
175+
// Extensions are copied across. The new extension node has the module
176+
// from the original, and the second child will be added as we traverse
177+
// the next node in the tree.
178+
auto copy = dem.createNode(Kind::Extension);
179+
auto module = current->getChild(0);
180+
if (module == nullptr || module->getKind() != Kind::Module)
181+
return nullptr;
182+
copy->addChild(module, dem);
183+
addNode(copy);
184+
current = current->getChild(1);
185+
break;
186+
}
187+
case Kind::Module: {
188+
// Module contents are always in the form we want, so we can incorporate
189+
// this node verbatim and terminate the walk.
190+
addNode(current);
191+
current = nullptr;
192+
break;
193+
}
194+
case Kind::Protocol:
195+
case Kind::OpaqueType:
196+
case Kind::Class:
197+
case Kind::Structure:
198+
case Kind::Enum:
199+
case Kind::TypeAlias:
200+
case Kind::OtherNominalType: {
201+
// Type nodes are copied across with the kind always set to
202+
// OtherNominalType.
203+
auto copy = dem.createNode(Kind::OtherNominalType);
204+
auto identifier = current->getChild(1);
205+
if (identifier == nullptr || identifier->getKind() != Kind::Identifier)
206+
return nullptr;
207+
copy->addChild(identifier, dem);
208+
addNode(copy);
209+
current = current->getChild(0);
210+
break;
211+
}
212+
213+
default:
214+
// If we don't know about this node, continue the walk with its first
215+
// child.
216+
current = current->getFirstChild();
217+
break;
218+
}
219+
}
220+
221+
return result;
222+
}
223+
88224
} // namespace swift
89225

90226
// Validate the prespecialized metadata map by building each entry dynamically

0 commit comments

Comments
 (0)