Skip to content

Commit c3391ce

Browse files
committed
[6.0][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. Enhance the environment variable code to track whether a variable was set at all. This allows SWIFT_DEBUG_ENABLE_LIB_PRESPECIALIZED to override the default in either direction. 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 7616da0 commit c3391ce

File tree

8 files changed

+798
-185
lines changed

8 files changed

+798
-185
lines changed

include/swift/Runtime/EnvironmentVariables.h

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,24 @@ extern swift::once_t initializeToken;
3030
using string = const char *;
3131

3232
// Declare backing variables.
33-
#define VARIABLE(name, type, defaultValue, help) extern type name ## _variable;
33+
#define VARIABLE(name, type, defaultValue, help) \
34+
extern type name##_variable; \
35+
extern bool name##_isSet_variable;
3436
#include "../../../stdlib/public/runtime/EnvironmentVariables.def"
3537

36-
// Define getter functions.
38+
// Define getter functions. This creates one function with the same name as the
39+
// variable which returns the value set for that variable, and second function
40+
// ending in _isSet which returns a boolean indicating whether the variable was
41+
// set at all, to allow detecting when the variable was explicitly set to the
42+
// same value as the default.
3743
#define VARIABLE(name, type, defaultValue, help) \
3844
inline type name() { \
3945
swift::once(initializeToken, initialize, nullptr); \
4046
return name##_variable; \
47+
} \
48+
inline bool name##_isSet() { \
49+
swift::once(initializeToken, initialize, nullptr); \
50+
return name##_isSet_variable; \
4151
}
4252
#include "../../../stdlib/public/runtime/EnvironmentVariables.def"
4353

include/swift/Runtime/LibPrespecialized.h

Lines changed: 151 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,44 @@ 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, the runtime should default to using the descriptor
61+
// map. When not set, default to turning off the descriptor map.
62+
OptionFlagDescriptorMapDefaultOn = 1ULL << 1,
63+
64+
// When this flag is set, descriptorMap is not comprehensive, meaning that
65+
// a negative lookup result is not a definitive failure.
66+
OptionFlagDescriptorMapNotComprehensive = 1ULL << 2,
5367
};
5468

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

5880
using MetadataMap = PrebuiltStringMap<const char *, Metadata *, stringIsNull>;
@@ -73,18 +95,141 @@ struct LibPrespecializedData {
7395
return pointerKeyedMetadataMap;
7496
}
7597

76-
typename Runtime::StoredSize getOptionFlags() const {
77-
if (minorVersion < minorVersionWithOptionFlags)
78-
return 0;
79-
return optionFlags;
98+
using DescriptorMap =
99+
PrebuiltAuxDataImplicitStringMap<TargetPointer<Runtime, const void>,
100+
uint16_t>;
101+
102+
const DescriptorMap *getDescriptorMap() const {
103+
if (minorVersion < minorVersionWithDescriptorMap)
104+
return nullptr;
105+
return reinterpret_cast<const DescriptorMap *>(descriptorMap);
80106
}
81107
};
82108

109+
enum class LibPrespecializedLookupResult {
110+
// We found something.
111+
Found,
112+
113+
// We didn't find anything, and we know it's not in the shared cache.
114+
DefinitiveNotFound,
115+
116+
// We didn't find anything, but we couldn't rule out the shared cache. Caller
117+
// must do a full search.
118+
NonDefinitiveNotFound,
119+
};
120+
83121
const LibPrespecializedData<InProcess> *getLibPrespecializedData();
122+
84123
Metadata *getLibPrespecializedMetadata(const TypeContextDescriptor *description,
85124
const void *const *arguments);
86125
void libPrespecializedImageLoaded();
87126

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

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

0 commit comments

Comments
 (0)