Skip to content

Commit dbcab4b

Browse files
authored
Merge pull request #15590 from jckarter/deserialize-clang-importer-witness-tables-ii
IRGen: Deserialize SIL witness tables and shared-linkage definitions by need.
2 parents b76fdef + 73895a3 commit dbcab4b

File tree

11 files changed

+122
-11
lines changed

11 files changed

+122
-11
lines changed

include/swift/AST/SILOptions.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@ class SILOptions {
4545
enum LinkingMode {
4646
/// Skip SIL linking.
4747
LinkNone,
48+
49+
/// Link only the given function without recursively visiting its uses.
50+
LinkThisFunctionOnly,
4851

4952
/// Perform normal SIL linking.
5053
LinkNormal,

include/swift/SIL/SILModule.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -715,6 +715,11 @@ class SILModule {
715715
bool isDefaultAtomic() const {
716716
return ! getOptions().AssumeSingleThreaded;
717717
}
718+
719+
/// Returns true if SIL entities associated with declarations in the given
720+
/// declaration context ought to be serialized as part of this module.
721+
bool shouldSerializeEntitiesAssociatedWithDeclContext(const DeclContext *DC)
722+
const;
718723
};
719724

720725
inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const SILModule &M){

lib/IRGen/GenDecl.cpp

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1054,7 +1054,8 @@ void IRGenerator::emitGlobalTopLevel() {
10541054
unsigned nextOrderNumber = 0;
10551055
for (auto &silFn : PrimaryIGM->getSILModule().getFunctions()) {
10561056
// Don't bother adding external declarations to the function order.
1057-
if (!silFn.isDefinition()) continue;
1057+
if (!silFn.isDefinition())
1058+
continue;
10581059
FunctionOrder.insert(std::make_pair(&silFn, nextOrderNumber++));
10591060
}
10601061

@@ -1204,6 +1205,15 @@ void IRGenerator::emitLazyDefinitions() {
12041205
}
12051206

12061207
void IRGenerator::addLazyFunction(SILFunction *f) {
1208+
// If the function is a shared declaration, it may have a body to
1209+
// deserialize.
1210+
if (!f->isDefinition() && hasSharedVisibility(f->getLinkage())) {
1211+
SIL.linkFunction(f, SILOptions::LinkingMode::LinkThisFunctionOnly);
1212+
if (!f->isDefinition())
1213+
return;
1214+
FunctionOrder.insert({f, FunctionOrder.size()});
1215+
}
1216+
12071217
// Add it to the queue if it hasn't already been put there.
12081218
if (!LazilyEmittedFunctions.insert(f).second)
12091219
return;
@@ -2241,8 +2251,8 @@ llvm::Function *IRGenModule::getAddrOfSILFunction(SILFunction *f,
22412251
clangAddr = getAddrOfClangGlobalDecl(globalDecl, forDefinition);
22422252
}
22432253

2244-
bool isDefinition = f->isDefinition();
2245-
bool hasOrderNumber = isDefinition;
2254+
bool isDefinition = f->isDefinition() || hasSharedVisibility(f->getLinkage());
2255+
bool hasOrderNumber = f->isDefinition();
22462256
unsigned orderNumber = ~0U;
22472257
llvm::Function *insertBefore = nullptr;
22482258

@@ -2273,8 +2283,9 @@ llvm::Function *IRGenModule::getAddrOfSILFunction(SILFunction *f,
22732283
}
22742284

22752285
// Otherwise, if we have a lazy definition for it, be sure to queue that up.
2276-
} else if (isDefinition && !forDefinition && !f->isPossiblyUsedExternally() &&
2277-
!hasCodeCoverageInstrumentation(*f, getSILModule())) {
2286+
} else if (isDefinition
2287+
&& !forDefinition && !f->isPossiblyUsedExternally()
2288+
&& !hasCodeCoverageInstrumentation(*f, getSILModule())) {
22782289
IRGen.addLazyFunction(f);
22792290
}
22802291

@@ -4116,7 +4127,7 @@ llvm::StructType *IRGenModule::getGenericWitnessTableCacheTy() {
41164127
llvm::Function *
41174128
IRGenModule::getAddrOfWitnessTableAccessFunction(
41184129
const NormalProtocolConformance *conf,
4119-
ForDefinition_t forDefinition) {
4130+
ForDefinition_t forDefinition) {
41204131
IRGen.addLazyWitnessTable(conf);
41214132

41224133
LinkEntity entity = LinkEntity::forProtocolWitnessTableAccessFunction(conf);

lib/IRGen/IRGenModule.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "swift/IRGen/Linking.h"
2525
#include "swift/Runtime/RuntimeFnWrappersGen.h"
2626
#include "swift/Runtime/Config.h"
27+
#include "swift/SIL/FormalLinkage.h"
2728
#include "clang/AST/ASTContext.h"
2829
#include "clang/Basic/CharInfo.h"
2930
#include "clang/Basic/TargetInfo.h"
@@ -658,6 +659,9 @@ llvm::Module *IRGenModule::releaseModule() {
658659
}
659660

660661
bool IRGenerator::canEmitWitnessTableLazily(SILWitnessTable *wt) {
662+
if (LazilyEmittedWitnessTables.count(wt))
663+
return true;
664+
661665
if (Opts.UseJIT)
662666
return false;
663667

@@ -685,6 +689,21 @@ void IRGenerator::addLazyWitnessTable(const ProtocolConformance *Conf) {
685689
LazilyEmittedWitnessTables.insert(wt).second) {
686690
LazyWitnessTables.push_back(wt);
687691
}
692+
// Shared-linkage protocol conformances may not have been deserialized yet
693+
// if they were used by inlined code. See if we can deserialize it now.
694+
} else {
695+
auto linkage =
696+
getLinkageForProtocolConformance(Conf->getRootNormalConformance(),
697+
ForDefinition);
698+
if (hasSharedVisibility(linkage)) {
699+
SIL.createWitnessTableDeclaration(const_cast<ProtocolConformance*>(Conf),
700+
linkage);
701+
SILWitnessTable *wt = SIL.lookUpWitnessTable(Conf);
702+
if (wt && wt->isDefinition()
703+
&& LazilyEmittedWitnessTables.insert(wt).second) {
704+
LazyWitnessTables.push_back(wt);
705+
}
706+
}
688707
}
689708
}
690709

lib/SIL/Linker.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ bool SILLinkerVisitor::processFunction(SILFunction *F) {
4747

4848
++NumFuncLinked;
4949

50+
if (Mode == LinkingMode::LinkThisFunctionOnly)
51+
return true;
52+
5053
// Try to transitively deserialize everything referenced by this
5154
// function.
5255
Worklist.push_back(F);
@@ -68,6 +71,9 @@ bool SILLinkerVisitor::processFunction(StringRef Name) {
6871
return false;
6972

7073
++NumFuncLinked;
74+
75+
if (Mode == LinkingMode::LinkThisFunctionOnly)
76+
return true;
7177

7278
// Try to transitively deserialize everything referenced by NewFn.
7379
Worklist.push_back(NewFn);

lib/SIL/SILModule.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include "Linker.h"
2323
#include "swift/SIL/SILVisitor.h"
2424
#include "swift/SIL/SILValue.h"
25+
#include "swift/ClangImporter/ClangModule.h"
2526
#include "llvm/ADT/FoldingSet.h"
2627
#include "llvm/ADT/SmallString.h"
2728
#include "llvm/ADT/StringSwitch.h"
@@ -781,6 +782,23 @@ bool SILModule::isNoReturnBuiltinOrIntrinsic(Identifier Name) {
781782
}
782783
}
783784

785+
bool SILModule::
786+
shouldSerializeEntitiesAssociatedWithDeclContext(const DeclContext *DC) const {
787+
// Serialize entities associated with this module's associated context.
788+
if (DC->isChildContextOf(getAssociatedContext())) {
789+
return true;
790+
}
791+
792+
// Serialize entities associated with clang modules, since other entities
793+
// may depend on them, and someone who deserializes those entities may not
794+
// have their own copy.
795+
if (isa<ClangModuleUnit>(DC->getModuleScopeContext())) {
796+
return true;
797+
}
798+
799+
return false;
800+
}
801+
784802
/// Returns true if it is the OnoneSupport module.
785803
bool SILModule::isOnoneSupportModule() const {
786804
return getSwiftModule()->getName().str() == SWIFT_ONONE_SUPPORT;

lib/Serialization/Deserialization.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -625,6 +625,8 @@ NormalProtocolConformance *ModuleFile::readNormalConformance(
625625

626626
ASTContext &ctx = getContext();
627627
DeclContext *dc = getDeclContext(contextID);
628+
assert(!isa<ClangModuleUnit>(dc->getModuleScopeContext())
629+
&& "should not have serialized a conformance from a clang module");
628630
Type conformingType = dc->getDeclaredInterfaceType();
629631
PrettyStackTraceType trace(ctx, "reading conformance for", conformingType);
630632

lib/Serialization/Serialization.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1606,7 +1606,9 @@ Serializer::writeConformance(ProtocolConformanceRef conformanceRef,
16061606
switch (conformance->getKind()) {
16071607
case ProtocolConformanceKind::Normal: {
16081608
auto normal = cast<NormalProtocolConformance>(conformance);
1609-
if (!isDeclXRef(getDeclForContext(normal->getDeclContext()))) {
1609+
if (!isDeclXRef(getDeclForContext(normal->getDeclContext()))
1610+
&& !isa<ClangModuleUnit>(normal->getDeclContext()
1611+
->getModuleScopeContext())) {
16101612
// A normal conformance in this module file.
16111613
unsigned abbrCode = abbrCodes[NormalProtocolConformanceIdLayout::Code];
16121614
NormalProtocolConformanceIdLayout::emitRecord(Out, ScratchRecord,

lib/Serialization/SerializeSIL.cpp

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2426,29 +2426,32 @@ void SILSerializer::writeSILBlock(const SILModule *SILMod) {
24262426
assert(assocDC && "cannot serialize SIL without an associated DeclContext");
24272427
for (const SILVTable &vt : SILMod->getVTables()) {
24282428
if ((ShouldSerializeAll || vt.isSerialized()) &&
2429-
vt.getClass()->isChildContextOf(assocDC))
2429+
SILMod->shouldSerializeEntitiesAssociatedWithDeclContext(vt.getClass()))
24302430
writeSILVTable(vt);
24312431
}
24322432

24332433
// Write out property descriptors.
24342434
for (const SILProperty &prop : SILMod->getPropertyList()) {
24352435
if ((ShouldSerializeAll || prop.isSerialized()) &&
2436-
prop.getDecl()->getInnermostDeclContext()->isChildContextOf(assocDC))
2436+
SILMod->shouldSerializeEntitiesAssociatedWithDeclContext(
2437+
prop.getDecl()->getInnermostDeclContext()))
24372438
writeSILProperty(prop);
24382439
}
24392440

24402441
// Write out fragile WitnessTables.
24412442
for (const SILWitnessTable &wt : SILMod->getWitnessTables()) {
24422443
if ((ShouldSerializeAll || wt.isSerialized()) &&
2443-
wt.getConformance()->getDeclContext()->isChildContextOf(assocDC))
2444+
SILMod->shouldSerializeEntitiesAssociatedWithDeclContext(
2445+
wt.getConformance()->getDeclContext()))
24442446
writeSILWitnessTable(wt);
24452447
}
24462448

24472449
// Write out DefaultWitnessTables.
24482450
for (const SILDefaultWitnessTable &wt : SILMod->getDefaultWitnessTables()) {
24492451
// FIXME: Don't need to serialize private and internal default witness
24502452
// tables.
2451-
if (wt.getProtocol()->getDeclContext()->isChildContextOf(assocDC))
2453+
if (SILMod->shouldSerializeEntitiesAssociatedWithDeclContext(
2454+
wt.getProtocol()))
24522455
writeSILDefaultWitnessTable(wt);
24532456
}
24542457

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import Foundation
2+
3+
public struct RegEx {
4+
public let pattern: String
5+
fileprivate let regex: NSRegularExpression
6+
public typealias Options = NSRegularExpression.Options
7+
8+
public init(pattern: String, options: Options = []) throws {
9+
self.pattern = pattern
10+
self.regex = try NSRegularExpression(pattern: pattern, options: options)
11+
}
12+
13+
/// Returns a match group for the first match, or nil if there was no match.
14+
public func firstMatch(in string: String) -> [String]? {
15+
let nsString = string as NSString
16+
17+
return regex.firstMatch(in: string, range: NSMakeRange(0, nsString.length)).map { match -> [String] in
18+
return (1 ..< match.numberOfRanges).map { idx -> String in
19+
let range = match.range(at: idx)
20+
return range.location == NSNotFound ? "" : nsString.substring(with: range)
21+
}
22+
}
23+
}
24+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-swift-frontend -swift-version 4 -emit-module -o %t/regex.swiftmodule %S/Inputs/deserialize-clang-importer-witness-tables/regex.swift
3+
// RUN: %target-swift-frontend -swift-version 4 -emit-ir %s -I %t | %FileCheck %s
4+
5+
// REQUIRES: objc_interop
6+
7+
import regex
8+
9+
public func foo(line: String) {
10+
// The NSRegularExpressionOptions: SetAlgebra conformance is used indirectly
11+
// from the default argument expression passed to `RegEx(pattern:options:)`
12+
// below. Ensure that a local copy of the definition was deserialized
13+
// and lowered to IR.
14+
// CHECK-LABEL: define {{.*}} i8** @"$SSo26NSRegularExpressionOptionsVs10SetAlgebraSCWa"
15+
// CHECK-LABEL: define {{.*}} void @"$SSo26NSRegularExpressionOptionsVs10SetAlgebraSCsACPxycfCTW"
16+
let versionRegex = try! RegEx(pattern: "Apple")
17+
_ = versionRegex.firstMatch(in: line)
18+
}

0 commit comments

Comments
 (0)