Skip to content

Commit cb84e48

Browse files
committed
[ORC] Introduce JITSymbolFlags::HasMaterializeSideEffectsOnly flag.
This flag can be used to mark a symbol as existing only for the purpose of enabling materialization. Such a symbol can be looked up to trigger materialization with the lookup returning only once materialization is complete. Symbols with this flag will never resolve however (to avoid permanently polluting the symbol table), and should only be looked up using the SymbolLookupFlags::WeaklyReferencedSymbol flag. The primary use case for this flag is initialization symbols.
1 parent d38d06e commit cb84e48

File tree

12 files changed

+221
-54
lines changed

12 files changed

+221
-54
lines changed

llvm/include/llvm/ExecutionEngine/JITSymbol.h

+18-1
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,9 @@ class JITSymbolFlags {
8484
Absolute = 1U << 3,
8585
Exported = 1U << 4,
8686
Callable = 1U << 5,
87-
LLVM_MARK_AS_BITMASK_ENUM(/* LargestValue = */ Callable)
87+
MaterializationSideEffectsOnly = 1U << 6,
88+
LLVM_MARK_AS_BITMASK_ENUM( // LargestValue =
89+
MaterializationSideEffectsOnly)
8890
};
8991

9092
/// Default-construct a JITSymbolFlags instance.
@@ -146,6 +148,21 @@ class JITSymbolFlags {
146148
/// Returns true if the given symbol is known to be callable.
147149
bool isCallable() const { return (Flags & Callable) == Callable; }
148150

151+
/// Returns true if this symbol is a materialization-side-effects-only
152+
/// symbol. Such symbols do not have a real address. They exist to trigger
153+
/// and support synchronization of materialization side effects, e.g. for
154+
/// collecting initialization information. These symbols will vanish from
155+
/// the symbol table immediately upon reaching the ready state, and will
156+
/// appear to queries as if they were never defined (except that query
157+
/// callback execution will be delayed until they reach the ready state).
158+
/// MaterializationSideEffectOnly symbols should only be queried using the
159+
/// SymbolLookupFlags::WeaklyReferencedSymbol flag (see
160+
/// llvm/include/llvm/ExecutionEngine/Orc/Core.h).
161+
bool hasMaterializationSideEffectsOnly() const {
162+
return (Flags & MaterializationSideEffectsOnly) ==
163+
MaterializationSideEffectsOnly;
164+
}
165+
149166
/// Get the underlying flags value as an integer.
150167
UnderlyingType getRawFlagsValue() const {
151168
return static_cast<UnderlyingType>(Flags);

llvm/include/llvm/ExecutionEngine/Orc/Core.h

+35-9
Original file line numberDiff line numberDiff line change
@@ -483,6 +483,20 @@ class MaterializationResponsibility {
483483
/// callbacks, metadata).
484484
Error defineMaterializing(SymbolFlagsMap SymbolFlags);
485485

486+
/// Define the given symbols as non-existent, removing it from the symbol
487+
/// table and notifying any pending queries. Queries that lookup up the
488+
/// symbol using the SymbolLookupFlags::WeaklyReferencedSymbol flag will
489+
/// behave as if the symbol had not been matched in the first place. Queries
490+
/// that required this symbol will fail with a missing symbol definition
491+
/// error.
492+
///
493+
/// This method is intended to support cleanup of special symbols like
494+
/// initializer symbols: Queries using
495+
/// SymbolLookupFlags::WeaklyReferencedSymbol can be used to trigger their
496+
/// emission, and this method can be used to remove them from the JITDylib
497+
/// once materialization is complete.
498+
void defineNonExistent(ArrayRef<SymbolStringPtr> Symbols);
499+
486500
/// Notify all not-yet-emitted covered by this MaterializationResponsibility
487501
/// instance that an error has occurred.
488502
/// This will remove all symbols covered by this MaterializationResponsibilty
@@ -721,15 +735,6 @@ class AsynchronousSymbolQuery {
721735
void notifySymbolMetRequiredState(const SymbolStringPtr &Name,
722736
JITEvaluatedSymbol Sym);
723737

724-
/// Remove a symbol from the query. This is used to drop weakly referenced
725-
/// symbols that are not found.
726-
void dropSymbol(const SymbolStringPtr &Name) {
727-
assert(ResolvedSymbols.count(Name) &&
728-
"Redundant removal of weakly-referenced symbol");
729-
ResolvedSymbols.erase(Name);
730-
--OutstandingSymbolsCount;
731-
}
732-
733738
/// Returns true if all symbols covered by this query have been
734739
/// resolved.
735740
bool isComplete() const { return OutstandingSymbolsCount == 0; }
@@ -747,6 +752,8 @@ class AsynchronousSymbolQuery {
747752

748753
void removeQueryDependence(JITDylib &JD, const SymbolStringPtr &Name);
749754

755+
void dropSymbol(const SymbolStringPtr &Name);
756+
750757
bool canStillFail();
751758

752759
void handleFailed(Error Err);
@@ -1298,6 +1305,16 @@ auto JITDylib::withSearchOrderDo(Func &&F)
12981305
template <typename MaterializationUnitType>
12991306
Error JITDylib::define(std::unique_ptr<MaterializationUnitType> &&MU) {
13001307
assert(MU && "Can not define with a null MU");
1308+
1309+
if (MU->getSymbols().empty()) {
1310+
// Empty MUs are allowable but pathological, so issue a warning.
1311+
DEBUG_WITH_TYPE("orc", {
1312+
dbgs() << "Warning: Discarding empty MU " << MU->getName() << "\n";
1313+
});
1314+
return Error::success();
1315+
} else
1316+
DEBUG_WITH_TYPE("orc", dbgs() << "Defining MU " << MU->getName() << ":\n");
1317+
13011318
return ES.runSessionLocked([&, this]() -> Error {
13021319
if (auto Err = defineImpl(*MU))
13031320
return Err;
@@ -1320,6 +1337,15 @@ template <typename MaterializationUnitType>
13201337
Error JITDylib::define(std::unique_ptr<MaterializationUnitType> &MU) {
13211338
assert(MU && "Can not define with a null MU");
13221339

1340+
if (MU->getSymbols().empty()) {
1341+
// Empty MUs are allowable but pathological, so issue a warning.
1342+
DEBUG_WITH_TYPE("orc", {
1343+
dbgs() << "Warning: Discarding empty MU " << MU->getName() << "\n";
1344+
});
1345+
return Error::success();
1346+
} else
1347+
DEBUG_WITH_TYPE("orc", dbgs() << "Defining MU " << MU->getName() << ":\n");
1348+
13231349
return ES.runSessionLocked([&, this]() -> Error {
13241350
if (auto Err = defineImpl(*MU))
13251351
return Err;

llvm/include/llvm/ExecutionEngine/Orc/DebugUtils.h

+3
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ raw_ostream &operator<<(raw_ostream &OS, const SymbolNameSet &Symbols);
3838
/// Render a SymbolNameVector.
3939
raw_ostream &operator<<(raw_ostream &OS, const SymbolNameVector &Symbols);
4040

41+
/// Render an array of SymbolStringPtrs.
42+
raw_ostream &operator<<(raw_ostream &OS, ArrayRef<SymbolStringPtr> Symbols);
43+
4144
/// Render JITSymbolFlags.
4245
raw_ostream &operator<<(raw_ostream &OS, const JITSymbolFlags &Flags);
4346

llvm/lib/ExecutionEngine/Orc/Core.cpp

+59-10
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,13 @@ void AsynchronousSymbolQuery::notifySymbolMetRequiredState(
118118
assert(I != ResolvedSymbols.end() &&
119119
"Resolving symbol outside the requested set");
120120
assert(I->second.getAddress() == 0 && "Redundantly resolving symbol Name");
121-
I->second = std::move(Sym);
121+
122+
// If this is a materialization-side-effects-only symbol then drop it,
123+
// otherwise update its map entry with its resolved address.
124+
if (Sym.getFlags().hasMaterializationSideEffectsOnly())
125+
ResolvedSymbols.erase(I);
126+
else
127+
I->second = std::move(Sym);
122128
--OutstandingSymbolsCount;
123129
}
124130

@@ -159,6 +165,14 @@ void AsynchronousSymbolQuery::removeQueryDependence(
159165
QueryRegistrations.erase(QRI);
160166
}
161167

168+
void AsynchronousSymbolQuery::dropSymbol(const SymbolStringPtr &Name) {
169+
auto I = ResolvedSymbols.find(Name);
170+
assert(I != ResolvedSymbols.end() &&
171+
"Redundant removal of weakly-referenced symbol");
172+
ResolvedSymbols.erase(I);
173+
--OutstandingSymbolsCount;
174+
}
175+
162176
void AsynchronousSymbolQuery::detach() {
163177
ResolvedSymbols.clear();
164178
OutstandingSymbolsCount = 0;
@@ -186,6 +200,8 @@ Error MaterializationResponsibility::notifyResolved(const SymbolMap &Symbols) {
186200
auto I = SymbolFlags.find(KV.first);
187201
assert(I != SymbolFlags.end() &&
188202
"Resolving symbol outside this responsibility set");
203+
assert(!I->second.hasMaterializationSideEffectsOnly() &&
204+
"Can't resolve materialization-side-effects-only symbol");
189205
assert((KV.second.getFlags() & ~WeakFlags) == (I->second & ~WeakFlags) &&
190206
"Resolving symbol with incorrect flags");
191207
}
@@ -398,11 +414,13 @@ void ReExportsMaterializationUnit::materialize(
398414
SymbolAliasMap Aliases;
399415
};
400416

401-
// Build a list of queries to issue. In each round we build the largest set of
402-
// aliases that we can resolve without encountering a chain definition of the
403-
// form Foo -> Bar, Bar -> Baz. Such a form would deadlock as the query would
404-
// be waitin on a symbol that it itself had to resolve. Usually this will just
405-
// involve one round and a single query.
417+
// Build a list of queries to issue. In each round we build a query for the
418+
// largest set of aliases that we can resolve without encountering a chain of
419+
// aliases (e.g. Foo -> Bar, Bar -> Baz). Such a chain would deadlock as the
420+
// query would be waiting on a symbol that it itself had to resolve. Creating
421+
// a new query for each link in such a chain eliminates the possibility of
422+
// deadlock. In practice chains are likely to be rare, and this algorithm will
423+
// usually result in a single query to issue.
406424

407425
std::vector<std::pair<SymbolLookupSet, std::shared_ptr<OnResolveInfo>>>
408426
QueryInfos;
@@ -419,7 +437,10 @@ void ReExportsMaterializationUnit::materialize(
419437
continue;
420438

421439
ResponsibilitySymbols.insert(KV.first);
422-
QuerySymbols.add(KV.second.Aliasee);
440+
QuerySymbols.add(KV.second.Aliasee,
441+
KV.second.AliasFlags.hasMaterializationSideEffectsOnly()
442+
? SymbolLookupFlags::WeaklyReferencedSymbol
443+
: SymbolLookupFlags::RequiredSymbol);
423444
QueryAliases[KV.first] = std::move(KV.second);
424445
}
425446

@@ -468,8 +489,13 @@ void ReExportsMaterializationUnit::materialize(
468489
if (Result) {
469490
SymbolMap ResolutionMap;
470491
for (auto &KV : QueryInfo->Aliases) {
471-
assert(Result->count(KV.second.Aliasee) &&
492+
assert((KV.second.AliasFlags.hasMaterializationSideEffectsOnly() ||
493+
Result->count(KV.second.Aliasee)) &&
472494
"Result map missing entry?");
495+
// Don't try to resolve materialization-side-effects-only symbols.
496+
if (KV.second.AliasFlags.hasMaterializationSideEffectsOnly())
497+
continue;
498+
473499
ResolutionMap[KV.first] = JITEvaluatedSymbol(
474500
(*Result)[KV.second.Aliasee].getAddress(), KV.second.AliasFlags);
475501
}
@@ -906,7 +932,9 @@ Error JITDylib::emit(const SymbolFlagsMap &Emitted) {
906932
auto &SymEntry = SymI->second;
907933

908934
// Move symbol to the emitted state.
909-
assert(SymEntry.getState() == SymbolState::Resolved &&
935+
assert(((SymEntry.getFlags().hasMaterializationSideEffectsOnly() &&
936+
SymEntry.getState() == SymbolState::Materializing) ||
937+
SymEntry.getState() == SymbolState::Resolved) &&
910938
"Emitting from state other than Resolved");
911939
SymEntry.setState(SymbolState::Emitted);
912940

@@ -1311,6 +1339,12 @@ Error JITDylib::lodgeQueryImpl(MaterializationUnitList &MUs,
13111339
if (SymI == Symbols.end())
13121340
return false;
13131341

1342+
// If we match against a materialization-side-effects only symbol then
1343+
// make sure it is weakly-referenced. Otherwise bail out with an error.
1344+
if (SymI->second.getFlags().hasMaterializationSideEffectsOnly() &&
1345+
SymLookupFlags != SymbolLookupFlags::WeaklyReferencedSymbol)
1346+
return make_error<SymbolsNotFound>(SymbolNameVector({Name}));
1347+
13141348
// If this is a non exported symbol and we're matching exported symbols
13151349
// only then skip this symbol without removal.
13161350
if (!SymI->second.getFlags().isExported() &&
@@ -1580,6 +1614,9 @@ JITDylib::JITDylib(ExecutionSession &ES, std::string Name)
15801614
}
15811615

15821616
Error JITDylib::defineImpl(MaterializationUnit &MU) {
1617+
1618+
LLVM_DEBUG({ dbgs() << " " << MU.getSymbols() << "\n"; });
1619+
15831620
SymbolNameSet Duplicates;
15841621
std::vector<SymbolStringPtr> ExistingDefsOverridden;
15851622
std::vector<SymbolStringPtr> MUDefsOverridden;
@@ -1604,14 +1641,26 @@ Error JITDylib::defineImpl(MaterializationUnit &MU) {
16041641
}
16051642

16061643
// If there were any duplicate definitions then bail out.
1607-
if (!Duplicates.empty())
1644+
if (!Duplicates.empty()) {
1645+
LLVM_DEBUG(
1646+
{ dbgs() << " Error: Duplicate symbols " << Duplicates << "\n"; });
16081647
return make_error<DuplicateDefinition>(std::string(**Duplicates.begin()));
1648+
}
16091649

16101650
// Discard any overridden defs in this MU.
1651+
LLVM_DEBUG({
1652+
if (!MUDefsOverridden.empty())
1653+
dbgs() << " Defs in this MU overridden: " << MUDefsOverridden << "\n";
1654+
});
16111655
for (auto &S : MUDefsOverridden)
16121656
MU.doDiscard(*this, S);
16131657

16141658
// Discard existing overridden defs.
1659+
LLVM_DEBUG({
1660+
if (!ExistingDefsOverridden.empty())
1661+
dbgs() << " Existing defs overridden by this MU: " << MUDefsOverridden
1662+
<< "\n";
1663+
});
16151664
for (auto &S : ExistingDefsOverridden) {
16161665

16171666
auto UMII = UnmaterializedInfos.find(S);

llvm/lib/ExecutionEngine/Orc/DebugUtils.cpp

+4
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,10 @@ raw_ostream &operator<<(raw_ostream &OS, const SymbolNameVector &Symbols) {
150150
return OS << printSequence(Symbols, '[', ']', PrintAll<SymbolStringPtr>());
151151
}
152152

153+
raw_ostream &operator<<(raw_ostream &OS, ArrayRef<SymbolStringPtr> Symbols) {
154+
return OS << printSequence(Symbols, '[', ']', PrintAll<SymbolStringPtr>());
155+
}
156+
153157
raw_ostream &operator<<(raw_ostream &OS, const JITSymbolFlags &Flags) {
154158
if (Flags.hasError())
155159
OS << "[*ERROR*]";

llvm/lib/ExecutionEngine/Orc/LLJIT.cpp

+3-2
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ class GenericLLVMIRPlatformSupport : public LLJIT::PlatformSupport {
176176

177177
Error notifyAdding(JITDylib &JD, const MaterializationUnit &MU) {
178178
if (auto &InitSym = MU.getInitializerSymbol())
179-
InitSymbols[&JD].add(InitSym);
179+
InitSymbols[&JD].add(InitSym, SymbolLookupFlags::WeaklyReferencedSymbol);
180180
else {
181181
// If there's no identified init symbol attached, but there is a symbol
182182
// with the GenericIRPlatform::InitFunctionPrefix, then treat that as
@@ -185,7 +185,8 @@ class GenericLLVMIRPlatformSupport : public LLJIT::PlatformSupport {
185185
// map (which holds the names of the symbols to execute).
186186
for (auto &KV : MU.getSymbols())
187187
if ((*KV.first).startswith(*InitFunctionPrefix)) {
188-
InitSymbols[&JD].add(KV.first);
188+
InitSymbols[&JD].add(KV.first,
189+
SymbolLookupFlags::WeaklyReferencedSymbol);
189190
InitFunctions[&JD].add(KV.first);
190191
}
191192
}

llvm/lib/ExecutionEngine/Orc/Layer.cpp

+13-5
Original file line numberDiff line numberDiff line change
@@ -81,11 +81,19 @@ IRMaterializationUnit::IRMaterializationUnit(
8181

8282
// If we need an init symbol for this module then create one.
8383
if (!llvm::empty(getStaticInitGVs(M))) {
84-
std::string InitSymbolName;
85-
raw_string_ostream(InitSymbolName)
86-
<< "$." << M.getModuleIdentifier() << ".__inits";
87-
InitSymbol = ES.intern(InitSymbolName);
88-
SymbolFlags[InitSymbol] = JITSymbolFlags();
84+
size_t Counter = 0;
85+
86+
while (true) {
87+
std::string InitSymbolName;
88+
raw_string_ostream(InitSymbolName)
89+
<< "$." << M.getModuleIdentifier() << ".__inits." << Counter++;
90+
InitSymbol = ES.intern(InitSymbolName);
91+
if (SymbolFlags.count(InitSymbol))
92+
continue;
93+
SymbolFlags[InitSymbol] =
94+
JITSymbolFlags::MaterializationSideEffectsOnly;
95+
break;
96+
}
8997
}
9098
});
9199
}

llvm/lib/ExecutionEngine/Orc/MachOPlatform.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,8 @@ Error MachOPlatform::notifyAdding(JITDylib &JD, const MaterializationUnit &MU) {
164164
if (!InitSym)
165165
return Error::success();
166166

167-
RegisteredInitSymbols[&JD].add(InitSym);
167+
RegisteredInitSymbols[&JD].add(InitSym,
168+
SymbolLookupFlags::WeaklyReferencedSymbol);
168169
LLVM_DEBUG({
169170
dbgs() << "MachOPlatform: Registered init symbol " << *InitSym << " for MU "
170171
<< MU.getName() << "\n";

llvm/lib/ExecutionEngine/Orc/Mangling.cpp

+13-5
Original file line numberDiff line numberDiff line change
@@ -130,11 +130,19 @@ getObjectSymbolInfo(ExecutionSession &ES, MemoryBufferRef ObjBuffer) {
130130
for (auto &Sec : MachOObj.sections()) {
131131
auto SecType = MachOObj.getSectionType(Sec);
132132
if ((SecType & MachO::SECTION_TYPE) == MachO::S_MOD_INIT_FUNC_POINTERS) {
133-
std::string InitSymString;
134-
raw_string_ostream(InitSymString)
135-
<< "$." << ObjBuffer.getBufferIdentifier() << ".__inits";
136-
InitSymbol = ES.intern(InitSymString);
137-
SymbolFlags[InitSymbol] = JITSymbolFlags();
133+
size_t Counter = 0;
134+
while (true) {
135+
std::string InitSymString;
136+
raw_string_ostream(InitSymString)
137+
<< "$." << ObjBuffer.getBufferIdentifier() << ".__inits."
138+
<< Counter++;
139+
InitSymbol = ES.intern(InitSymString);
140+
if (SymbolFlags.count(InitSymbol))
141+
continue;
142+
SymbolFlags[InitSymbol] =
143+
JITSymbolFlags::MaterializationSideEffectsOnly;
144+
break;
145+
}
138146
break;
139147
}
140148
}

0 commit comments

Comments
 (0)