Skip to content

Commit 46e3590

Browse files
committed
Merge pull request #2176 from swiftix/master
Improve the implementation of pre-specializations
2 parents 7dc6855 + 27bcabc commit 46e3590

File tree

10 files changed

+210
-43
lines changed

10 files changed

+210
-43
lines changed

include/swift/Serialization/SerializedSILLoader.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ class SerializedSILLoader {
9494
SILFunction *
9595
lookupSILFunction(StringRef Name, bool declarationOnly = false,
9696
SILLinkage linkage = SILLinkage::Private);
97+
bool hasSILFunction(StringRef Name, SILLinkage linkage = SILLinkage::Private);
9798
SILVTable *lookupVTable(Identifier Name);
9899
SILVTable *lookupVTable(const ClassDecl *C) {
99100
return lookupVTable(C->getName());

lib/SIL/Linker.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,10 @@ SILFunction *SILLinkerVisitor::lookupFunction(StringRef Name,
139139
return NewFn;
140140
}
141141

142+
/// Process Decl, recursively deserializing any thing Decl may reference.
143+
bool SILLinkerVisitor::hasFunction(StringRef Name, SILLinkage Linkage) {
144+
return Loader->hasSILFunction(Name, Linkage);
145+
}
142146

143147
/// Deserialize the VTable mapped to C if it exists and all SIL the VTable
144148
/// transitively references.

lib/SIL/Linker.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,10 @@ class SILLinkerVisitor : public SILInstructionVisitor<SILLinkerVisitor, bool> {
5858
/// this Name.
5959
SILFunction *lookupFunction(StringRef Name, SILLinkage Linkage);
6060

61+
/// Process Name, try to check if there is a declaration of a function
62+
/// with this Name.
63+
bool hasFunction(StringRef Name, SILLinkage Linkage);
64+
6165
/// Process Decl, recursively deserializing any thing that
6266
/// the SILFunction corresponding to Decl may reference.
6367
bool processDeclRef(SILDeclRef Decl);

lib/SIL/SILModule.cpp

Lines changed: 42 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -484,12 +484,48 @@ bool SILModule::linkFunction(StringRef Name, SILModule::LinkingMode Mode) {
484484
}
485485

486486
SILFunction *SILModule::hasFunction(StringRef Name, SILLinkage Linkage) {
487-
assert(!lookUpFunction(Name) && "hasFunction should be only called for "
488-
"functions that are not contained in the "
489-
"SILModule yet");
490-
return SILLinkerVisitor(*this, getSILLoader(),
491-
SILModule::LinkingMode::LinkNormal)
492-
.lookupFunction(Name, Linkage);
487+
SILFunction *F = lookUpFunction(Name);
488+
489+
assert((Linkage == SILLinkage::Public ||
490+
Linkage == SILLinkage::PublicExternal) &&
491+
"Only a lookup of public functions is supported currently");
492+
493+
// Nothing to do if the current module has a required function
494+
// with a proper linkage.
495+
if (F && F->getLinkage() == Linkage)
496+
return F;
497+
498+
assert((!F || F->getLinkage() != Linkage) &&
499+
"hasFunction should be only called for functions that are not "
500+
"contained in the SILModule yet or do not have a required linkage");
501+
(void)F;
502+
503+
SILLinkerVisitor Visitor(*this, getSILLoader(),
504+
SILModule::LinkingMode::LinkNormal);
505+
506+
if (Visitor.hasFunction(Name, Linkage)) {
507+
if (!F) {
508+
// Load the function.
509+
F = Visitor.lookupFunction(Name, Linkage);
510+
assert(F && "Function should be present");
511+
assert(F->getLinkage() == Linkage &&
512+
"SILFunction has a wrong linkage");
513+
}
514+
// If a function exists already and it is a non-optimizing
515+
// compilaiton, simply convert it into an external declaration,
516+
// so that a compiled version from the shared library is used.
517+
if (F->isDefinition() &&
518+
F->getModule().getOptions().Optimization <
519+
SILOptions::SILOptMode::Optimize) {
520+
F->convertToDeclaration();
521+
}
522+
if (F->isExternalDeclaration())
523+
F->setFragile(IsFragile_t::IsNotFragile);
524+
F->setLinkage(Linkage);
525+
} else {
526+
F = nullptr;
527+
}
528+
return F;
493529
}
494530

495531
void SILModule::linkAllWitnessTables() {

lib/SILOptimizer/IPO/UsePrespecialized.cpp

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,9 @@ bool UsePrespecialized::replaceByPrespecialized(SILFunction &F) {
7575
if (!ReferencedF)
7676
continue;
7777

78+
DEBUG(llvm::dbgs() << "Trying to use specialized function for:\n";
79+
AI.getInstruction()->dumpInContext());
80+
7881
// Check if it is a call of a generic function.
7982
// If this is the case, check if there is a specialization
8083
// available for it already and use this specialization
@@ -114,12 +117,22 @@ bool UsePrespecialized::replaceByPrespecialized(SILFunction &F) {
114117
// If we already have this specialization, reuse it.
115118
auto PrevF = M.lookUpFunction(ClonedName);
116119
if (PrevF) {
120+
DEBUG(llvm::dbgs() << "Found a specialization: " << ClonedName << "\n");
117121
if (PrevF->getLinkage() != SILLinkage::SharedExternal)
118122
NewF = PrevF;
119-
} else {
123+
else {
124+
DEBUG(llvm::dbgs() << "Wrong linkage: " << (int)PrevF->getLinkage()
125+
<< "\n");
126+
}
127+
}
128+
129+
if (!PrevF || !NewF) {
120130
// Check for the existence of this function in another module without
121131
// loading the function body.
122132
PrevF = lookupPrespecializedSymbol(M, ClonedName);
133+
DEBUG(llvm::dbgs()
134+
<< "Checked if there is a specialization in a different module: "
135+
<< PrevF << "\n");
123136
if (!PrevF)
124137
continue;
125138
NewF = PrevF;
@@ -128,10 +141,12 @@ bool UsePrespecialized::replaceByPrespecialized(SILFunction &F) {
128141
if (!NewF)
129142
continue;
130143

144+
assert(NewF->isExternalDeclaration() &&
145+
"Prespecialized function should be an external declaration");
146+
131147
// An existing specialization was found.
132-
DEBUG(
133-
llvm::dbgs() << "Found a specialization of " << ReferencedF->getName()
134-
<< " : " << NewF->getName() << "\n");
148+
DEBUG(llvm::dbgs() << "Found a specialization of " << ReferencedF->getName()
149+
<< " : " << NewF->getName() << "\n");
135150

136151
auto NewAI = replaceWithSpecializedFunction(AI, NewF, ReInfo);
137152
AI.getInstruction()->replaceAllUsesWith(NewAI.getInstruction());

lib/SILOptimizer/Utils/Generics.cpp

Lines changed: 61 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@ ReabstractionInfo::ReabstractionInfo(SILFunction *OrigF,
4141
if (hasUnboundGenericTypes(InterfaceSubs)) {
4242
DEBUG(llvm::dbgs() <<
4343
" Cannot specialize with unbound interface substitutions.\n");
44+
DEBUG(for (auto Sub : ParamSubs) {
45+
Sub.dump();
46+
});
4447
return;
4548
}
4649
if (hasDynamicSelfTypes(InterfaceSubs)) {
@@ -167,8 +170,12 @@ SILFunction *GenericFuncSpecializer::lookupSpecialization() {
167170
assert(ReInfo.getSpecializedType()
168171
== SpecializedF->getLoweredFunctionType() &&
169172
"Previously specialized function does not match expected type.");
173+
DEBUG(llvm::dbgs() << "Found an existing specialization for: " << ClonedName
174+
<< "\n");
170175
return SpecializedF;
171176
}
177+
DEBUG(llvm::dbgs() << "Could not find an existing specialization for: "
178+
<< ClonedName << "\n");
172179
return nullptr;
173180
}
174181

@@ -400,7 +407,8 @@ void swift::trySpecializeApplyOfGeneric(
400407
auto *F = Apply.getInstruction()->getFunction();
401408
auto *RefF = cast<FunctionRefInst>(Apply.getCallee())->getReferencedFunction();
402409

403-
DEBUG(llvm::dbgs() << " ApplyInst: " << *Apply.getInstruction());
410+
DEBUG(llvm::dbgs() << " ApplyInst:\n";
411+
Apply.getInstruction()->dumpInContext());
404412

405413
// If the caller is fragile but the callee is not, bail out.
406414
// Specializations have shared linkage, which means they do
@@ -470,6 +478,9 @@ void swift::trySpecializeApplyOfGeneric(
470478
== SpecializedF->isFragile() &&
471479
"Previously specialized function does not match expected "
472480
"resilience level.");
481+
// Even if the pre-specialization exists already, try to preserve it
482+
// if it is whitelisted.
483+
linkSpecialization(M, SpecializedF);
473484
} else {
474485
SpecializedF = FuncSpecializer.tryCreateSpecialization();
475486
if (!SpecializedF)
@@ -534,8 +545,29 @@ void swift::trySpecializeApplyOfGeneric(
534545
// This uses the SIL linker to checks for the does not load the body of the pres
535546
// =============================================================================
536547

548+
static void keepSpecializationAsPublic(SILFunction *F) {
549+
DEBUG(auto DemangledNameString =
550+
swift::Demangle::demangleSymbolAsString(F->getName());
551+
StringRef DemangledName = DemangledNameString;
552+
llvm::dbgs() << "Keep specialization public: " << DemangledName << " : "
553+
<< F->getName() << "\n");
554+
// Make it public, so that others can refer to it.
555+
//
556+
// NOTE: This function may refer to non-public symbols, which may lead to
557+
// problems, if you ever try to inline this function. Therefore, these
558+
// specializations should only be used to refer to them, but should never
559+
// be inlined! The general rule could be: Never inline specializations
560+
// from stdlib!
561+
//
562+
// NOTE: Making these specializations public at this point breaks
563+
// some optimizations. Therefore, just mark the function.
564+
// DeadFunctionElimination pass will check if the function is marked
565+
// and preserve it if required.
566+
F->setKeepAsPublic(true);
567+
}
568+
537569
/// Link a specialization for generating prespecialized code.
538-
///
570+
///
539571
/// For now, it is performed only for specializations in the
540572
/// standard library. But in the future, one could think of
541573
/// maintaining a cache of optimized specializations.
@@ -545,34 +577,14 @@ void swift::trySpecializeApplyOfGeneric(
545577
/// the library, but only used only by client code compiled at -Onone. They
546578
/// should be never inlined.
547579
static bool linkSpecialization(SILModule &M, SILFunction *F) {
580+
if (F->isKeepAsPublic())
581+
return true;
548582
// Do not remove functions from the white-list. Keep them around.
549583
// Change their linkage to public, so that other applications can refer to it.
550-
551584
if (M.getOptions().Optimization >= SILOptions::SILOptMode::Optimize &&
552-
F->getLinkage() != SILLinkage::Public &&
553585
F->getModule().getSwiftModule()->getName().str() == SWIFT_ONONE_SUPPORT) {
554-
if (F->getLinkage() != SILLinkage::Public &&
555-
isWhitelistedSpecialization(F->getName())) {
556-
557-
DEBUG(
558-
auto DemangledNameString =
559-
swift::Demangle::demangleSymbolAsString(F->getName());
560-
StringRef DemangledName = DemangledNameString;
561-
llvm::dbgs() << "Keep specialization: " << DemangledName << " : "
562-
<< F->getName() << "\n");
563-
// Make it public, so that others can refer to it.
564-
//
565-
// NOTE: This function may refer to non-public symbols, which may lead to
566-
// problems, if you ever try to inline this function. Therefore, these
567-
// specializations should only be used to refer to them, but should never
568-
// be inlined! The general rule could be: Never inline specializations
569-
// from stdlib!
570-
//
571-
// NOTE: Making these specializations public at this point breaks
572-
// some optimizations. Therefore, just mark the function.
573-
// DeadFunctionElimination pass will check if the function is marked
574-
// and preserve it if required.
575-
F->setKeepAsPublic(true);
586+
if (isWhitelistedSpecialization(F->getName())) {
587+
keepSpecializationAsPublic(F);
576588
return true;
577589
}
578590
}
@@ -607,7 +619,10 @@ bool swift::isWhitelistedSpecialization(StringRef SpecName) {
607619

608620
StringRef DemangledName = DemangledNameString;
609621

622+
DEBUG(llvm::dbgs() << "Check if whitelisted: " << DemangledName << "\n");
623+
610624
auto pos = DemangledName.find("generic ", 0);
625+
auto oldpos = pos;
611626
if (pos == StringRef::npos)
612627
return false;
613628

@@ -618,11 +633,22 @@ bool swift::isWhitelistedSpecialization(StringRef SpecName) {
618633
buffer << STDLIB_NAME <<'.';
619634

620635
StringRef OfStr = buffer.str();
621-
622-
pos = DemangledName.find(OfStr, pos);
623-
624-
if (pos == StringRef::npos)
625-
return false;
636+
DEBUG(llvm::dbgs() << "Check substring: " << OfStr << "\n");
637+
638+
pos = DemangledName.find(OfStr, oldpos);
639+
640+
if (pos == StringRef::npos) {
641+
// Create "of (extension in Swift).Swift"
642+
llvm::SmallString<64> OfString;
643+
llvm::raw_svector_ostream buffer(OfString);
644+
buffer << "of (extension in " << STDLIB_NAME << "):";
645+
buffer << STDLIB_NAME << '.';
646+
OfStr = buffer.str();
647+
pos = DemangledName.find(OfStr, oldpos);
648+
DEBUG(llvm::dbgs() << "Check substring: " << OfStr << "\n");
649+
if (pos == StringRef::npos)
650+
return false;
651+
}
626652

627653
pos += OfStr.size();
628654

@@ -659,8 +685,10 @@ SILFunction *swift::lookupPrespecializedSymbol(SILModule &M,
659685
StringRef FunctionName) {
660686
// First check if the module contains a required specialization already.
661687
auto *Specialization = M.lookUpFunction(FunctionName);
662-
if (Specialization)
663-
return Specialization;
688+
if (Specialization) {
689+
if (Specialization->getLinkage() == SILLinkage::PublicExternal)
690+
return Specialization;
691+
}
664692

665693
// Then check if the required specialization can be found elsewhere.
666694
Specialization = lookupExistingSpecialization(M, FunctionName);

lib/Serialization/DeserializeSIL.cpp

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1798,6 +1798,68 @@ SILFunction *SILDeserializer::lookupSILFunction(SILFunction *InFunc) {
17981798
return Func;
17991799
}
18001800

1801+
/// Check for existence of a function with a given name and required linkage.
1802+
/// This function is modelled after readSILFunction. But it does not
1803+
/// create a SILFunction object.
1804+
bool SILDeserializer::hasSILFunction(StringRef Name,
1805+
SILLinkage Linkage) {
1806+
if (!FuncTable)
1807+
return false;
1808+
auto iter = FuncTable->find(Name);
1809+
if (iter == FuncTable->end())
1810+
return false;
1811+
1812+
// There is a function with the required name.
1813+
// Find out which linkage it has.
1814+
auto FID = *iter;
1815+
auto &cacheEntry = Funcs[FID-1];
1816+
if (cacheEntry.isFullyDeserialized() ||
1817+
(cacheEntry.isDeserialized()))
1818+
return cacheEntry.get()->getLinkage() == Linkage ||
1819+
Linkage == SILLinkage::Private;
1820+
1821+
BCOffsetRAII restoreOffset(SILCursor);
1822+
SILCursor.JumpToBit(cacheEntry.getOffset());
1823+
1824+
auto entry = SILCursor.advance(AF_DontPopBlockAtEnd);
1825+
if (entry.Kind == llvm::BitstreamEntry::Error) {
1826+
DEBUG(llvm::dbgs() << "Cursor advance error in hasSILFunction.\n");
1827+
MF->error();
1828+
return false;
1829+
}
1830+
1831+
SmallVector<uint64_t, 64> scratch;
1832+
StringRef blobData;
1833+
unsigned kind = SILCursor.readRecord(entry.ID, scratch, &blobData);
1834+
assert(kind == SIL_FUNCTION && "expect a sil function");
1835+
(void)kind;
1836+
1837+
// Read function properties only, e.g. its linkage and other attributes.
1838+
// TODO: If this results in any noticable performance problems, Cache the
1839+
// linkage to avoid re-reading it from the bitcode each time?
1840+
TypeID funcTyID;
1841+
unsigned rawLinkage, isTransparent, isFragile, isThunk, isGlobal,
1842+
inlineStrategy, effect, numSpecAttrs;
1843+
ArrayRef<uint64_t> SemanticsIDs;
1844+
SILFunctionLayout::readRecord(scratch, rawLinkage, isTransparent, isFragile,
1845+
isThunk, isGlobal, inlineStrategy, effect,
1846+
numSpecAttrs, funcTyID, SemanticsIDs);
1847+
auto linkage = fromStableSILLinkage(rawLinkage);
1848+
if (!linkage) {
1849+
DEBUG(llvm::dbgs() << "invalid linkage code " << rawLinkage
1850+
<< " for SIL function " << Name << "\n");
1851+
return false;
1852+
}
1853+
1854+
// Bail if it is not a required linkage.
1855+
if (linkage.getValue() != Linkage && Linkage != SILLinkage::Private)
1856+
return false;
1857+
1858+
DEBUG(llvm::dbgs() << "Found SIL Function: " << Name << "\n");
1859+
return true;
1860+
}
1861+
1862+
18011863
SILFunction *SILDeserializer::lookupSILFunction(StringRef name,
18021864
bool declarationOnly) {
18031865
if (!FuncTable)

lib/Serialization/DeserializeSIL.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ namespace swift {
117117
SILFunction *lookupSILFunction(SILFunction *InFunc);
118118
SILFunction *lookupSILFunction(StringRef Name,
119119
bool declarationOnly = false);
120+
bool hasSILFunction(StringRef Name, SILLinkage Linkage);
120121
SILVTable *lookupVTable(Identifier Name);
121122
SILWitnessTable *lookupWitnessTable(SILWitnessTable *wt);
122123
SILDefaultWitnessTable *

lib/Serialization/SerializedSILLoader.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,18 @@ SILFunction *SerializedSILLoader::lookupSILFunction(StringRef Name,
104104
return retVal;
105105
}
106106

107+
bool SerializedSILLoader::hasSILFunction(StringRef Name, SILLinkage Linkage) {
108+
// It is possible that one module has a declaration of a SILFunction, while
109+
// another has the full definition.
110+
SILFunction *retVal = nullptr;
111+
for (auto &Des : LoadedSILSections) {
112+
if (Des->hasSILFunction(Name, Linkage))
113+
return true;
114+
}
115+
return retVal;
116+
}
117+
118+
107119
SILVTable *SerializedSILLoader::lookupVTable(Identifier Name) {
108120
for (auto &Des : LoadedSILSections) {
109121
if (auto VT = Des->lookupVTable(Name))

0 commit comments

Comments
 (0)