Skip to content

Commit 83a3bd2

Browse files
committed
SILGen: Simplify delayed function emission
Just as with conformances, we can detect that a delayed function needs to be added to the queue from 'first principles' rather than walking the ExternalDefinitions list. This completely eliminates the ExternalDefinitions walk from SILGen, which has several advantages: - It fixes a source of quadratic behavior. In batch mode, type checking produces a list of external definitions shared across all primary files. Then, SILGen runs once per primary file, building a delayed emission map every time. - It allows SILGen to emit external definitions which only come into existence as a result of lazy conformance checking. Previously, anything that was added after SILGen performed its walk over the external definitions list would not be emitted.
1 parent 04db869 commit 83a3bd2

File tree

7 files changed

+73
-99
lines changed

7 files changed

+73
-99
lines changed

lib/SILGen/SILGen.cpp

Lines changed: 43 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -568,10 +568,6 @@ static SILFunction *getFunctionToInsertAfter(SILGenModule &SGM,
568568
return nullptr;
569569
}
570570

571-
static bool hasSILBody(FuncDecl *fd) {
572-
return fd->getBody(/*canSynthesize=*/false);
573-
}
574-
575571
static bool haveProfiledAssociatedFunction(SILDeclRef constant) {
576572
return constant.isDefaultArgGenerator() || constant.isForeign ||
577573
constant.isCurried;
@@ -610,7 +606,7 @@ static void setUpForProfiling(SILDeclRef constant, SILFunction *F,
610606
if (!haveProfiledAssociatedFunction(constant)) {
611607
if (constant.hasDecl()) {
612608
if (auto *fd = constant.getFuncDecl()) {
613-
if (hasSILBody(fd)) {
609+
if (fd->hasBody()) {
614610
F->createProfiler(fd, forDefinition);
615611
profiledNode = fd->getBody(/*canSynthesize=*/false);
616612
}
@@ -625,6 +621,32 @@ static void setUpForProfiling(SILDeclRef constant, SILFunction *F,
625621
}
626622
}
627623

624+
static bool isEmittedOnDemand(SILModule &M, SILDeclRef constant) {
625+
if (!constant.hasDecl())
626+
return false;
627+
628+
if (constant.isCurried ||
629+
constant.isForeign ||
630+
constant.isDirectReference)
631+
return false;
632+
633+
auto *d = constant.getDecl();
634+
auto *dc = d->getDeclContext()->getModuleScopeContext();
635+
636+
if (isa<ClangModuleUnit>(dc))
637+
return true;
638+
639+
if (auto *sf = dyn_cast<SourceFile>(dc))
640+
if (M.isWholeModule() || M.getAssociatedContext() == dc)
641+
return false;
642+
643+
if (auto *func = dyn_cast<FuncDecl>(d))
644+
if (func->hasForcedStaticDispatch())
645+
return true;
646+
647+
return false;
648+
}
649+
628650
SILFunction *SILGenModule::getFunction(SILDeclRef constant,
629651
ForDefinition_t forDefinition) {
630652
// If we already emitted the function, return it (potentially preparing it
@@ -645,6 +667,21 @@ SILFunction *SILGenModule::getFunction(SILDeclRef constant,
645667

646668
emittedFunctions[constant] = F;
647669

670+
if (isEmittedOnDemand(M, constant) &&
671+
!delayedFunctions.count(constant)) {
672+
auto *d = constant.getDecl();
673+
if (auto *func = dyn_cast<FuncDecl>(d)) {
674+
if (constant.kind == SILDeclRef::Kind::Func)
675+
emitFunction(func);
676+
} else if (auto *ctor = dyn_cast<ConstructorDecl>(d)) {
677+
// For factories, we don't need to emit a special thunk; the normal
678+
// foreign-to-native thunk is sufficient.
679+
if (!ctor->isFactoryInit() &&
680+
constant.kind == SILDeclRef::Kind::Allocator)
681+
emitConstructor(ctor);
682+
}
683+
}
684+
648685
// If we delayed emitting this function previously, we need it now.
649686
auto foundDelayed = delayedFunctions.find(constant);
650687
if (foundDelayed != delayedFunctions.end()) {
@@ -816,7 +853,7 @@ void SILGenModule::emitFunction(FuncDecl *fd) {
816853

817854
emitAbstractFuncDecl(fd);
818855

819-
if (hasSILBody(fd)) {
856+
if (fd->hasBody()) {
820857
FrontendStatsTracer Tracer(getASTContext().Stats, "SILGen-funcdecl", fd);
821858
PrettyStackTraceDecl stackTrace("emitting SIL for", fd);
822859

@@ -1736,13 +1773,6 @@ SILModule::constructSIL(ModuleDecl *mod, SILOptions &options, FileUnit *SF) {
17361773
M->getSILLoader()->getAllForModule(mod->getName(), nullptr);
17371774
}
17381775

1739-
// Emit external definitions used by this module.
1740-
for (size_t i = 0, e = mod->getASTContext().LastCheckedExternalDefinition;
1741-
i != e; ++i) {
1742-
auto def = mod->getASTContext().ExternalDefinitions[i];
1743-
SGM.emitExternalDefinition(def);
1744-
}
1745-
17461776
// Emit any delayed definitions that were forced.
17471777
// Emitting these may in turn force more definitions, so we have to take care
17481778
// to keep pumping the queues.

lib/SILGen/SILGen.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -263,9 +263,6 @@ class LLVM_LIBRARY_VISIBILITY SILGenModule : public ASTVisitor<SILGenModule> {
263263

264264
/// Add a global variable to the SILModule.
265265
void addGlobalVariable(VarDecl *global);
266-
267-
/// Emit SIL related to a Clang-imported declaration.
268-
void emitExternalDefinition(Decl *d);
269266

270267
/// Emit the ObjC-compatible entry point for a method.
271268
void emitObjCMethodThunk(FuncDecl *method);

lib/SILGen/SILGenDecl.cpp

Lines changed: 0 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1391,71 +1391,6 @@ CleanupHandle SILGenFunction::enterDeinitExistentialCleanup(
13911391
return Cleanups.getTopCleanup();
13921392
}
13931393

1394-
static bool isDeclaredInPrimaryFile(SILModule &M, Decl *d) {
1395-
auto *dc = d->getDeclContext();
1396-
if (auto *sf = dyn_cast<SourceFile>(dc->getModuleScopeContext()))
1397-
if (M.isWholeModule() || M.getAssociatedContext() == sf)
1398-
return true;
1399-
1400-
return false;
1401-
}
1402-
1403-
void SILGenModule::emitExternalDefinition(Decl *d) {
1404-
if (isDeclaredInPrimaryFile(M, d))
1405-
return;
1406-
1407-
switch (d->getKind()) {
1408-
case DeclKind::Func:
1409-
case DeclKind::Accessor: {
1410-
emitFunction(cast<FuncDecl>(d));
1411-
break;
1412-
}
1413-
case DeclKind::Constructor: {
1414-
auto C = cast<ConstructorDecl>(d);
1415-
// For factories, we don't need to emit a special thunk; the normal
1416-
// foreign-to-native thunk is sufficient.
1417-
if (C->isFactoryInit())
1418-
break;
1419-
1420-
emitConstructor(C);
1421-
break;
1422-
}
1423-
case DeclKind::Enum:
1424-
case DeclKind::Struct:
1425-
case DeclKind::Class:
1426-
case DeclKind::Protocol:
1427-
// Nothing to do in SILGen for external types.
1428-
break;
1429-
1430-
case DeclKind::Var:
1431-
// Imported static vars are handled solely in IRGen.
1432-
break;
1433-
1434-
case DeclKind::IfConfig:
1435-
case DeclKind::PoundDiagnostic:
1436-
case DeclKind::Extension:
1437-
case DeclKind::PatternBinding:
1438-
case DeclKind::EnumCase:
1439-
case DeclKind::EnumElement:
1440-
case DeclKind::TopLevelCode:
1441-
case DeclKind::TypeAlias:
1442-
case DeclKind::AssociatedType:
1443-
case DeclKind::GenericTypeParam:
1444-
case DeclKind::Param:
1445-
case DeclKind::Import:
1446-
case DeclKind::Subscript:
1447-
case DeclKind::Destructor:
1448-
case DeclKind::InfixOperator:
1449-
case DeclKind::PrefixOperator:
1450-
case DeclKind::PostfixOperator:
1451-
case DeclKind::PrecedenceGroup:
1452-
case DeclKind::Module:
1453-
case DeclKind::MissingMember:
1454-
case DeclKind::OpaqueType:
1455-
llvm_unreachable("Not a valid external definition for SILGen");
1456-
}
1457-
}
1458-
14591394
/// Create a LocalVariableInitialization for the uninitialized var.
14601395
InitializationPtr SILGenFunction::emitLocalVariableWithCleanup(
14611396
VarDecl *vd, Optional<MarkUninitializedInst::Kind> kind, unsigned ArgNo) {

test/ClangImporter/attr-swift_private.swift

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,15 @@ public func testInitializers() {
5353
_ = Bar(__: 1)
5454
}
5555

56+
// CHECK-LABEL: define linkonce_odr hidden {{.+}} @"$sSo3BarC8__noArgsABSgyt_tcfcTO"
57+
// CHECK: @"\01L_selector(initWithNoArgs)"
58+
// CHECK-LABEL: define linkonce_odr hidden {{.+}} @"$sSo3BarC8__oneArgABSgs5Int32V_tcfcTO"
59+
// CHECK: @"\01L_selector(initWithOneArg:)"
60+
// CHECK-LABEL: define linkonce_odr hidden {{.+}} @"$sSo3BarC9__twoArgs5otherABSgs5Int32V_AGtcfcTO"
61+
// CHECK: @"\01L_selector(initWithTwoArgs:other:)"
62+
// CHECK-LABEL: define linkonce_odr hidden {{.+}} @"$sSo3BarC2__ABSgs5Int32V_tcfcTO"
63+
// CHECK: @"\01L_selector(init:)"
64+
5665
// CHECK-LABEL: define{{( protected)?}} swiftcc void @{{.+}}18testFactoryMethods
5766
public func testFactoryMethods() {
5867
// CHECK: @"\01L_selector(fooWithOneArg:)"
@@ -95,15 +104,6 @@ public func testTopLevel() {
95104
// CHECK: %objc_class** @"OBJC_CLASS_REF_$_PrivFooSub"
96105
// CHECK: }
97106

98-
// CHECK-LABEL: define linkonce_odr hidden {{.+}} @"$sSo3BarC8__noArgsABSgyt_tcfcTO"
99-
// CHECK: @"\01L_selector(initWithNoArgs)"
100-
// CHECK-LABEL: define linkonce_odr hidden {{.+}} @"$sSo3BarC8__oneArgABSgs5Int32V_tcfcTO"
101-
// CHECK: @"\01L_selector(initWithOneArg:)"
102-
// CHECK-LABEL: define linkonce_odr hidden {{.+}} @"$sSo3BarC9__twoArgs5otherABSgs5Int32V_AGtcfcTO"
103-
// CHECK: @"\01L_selector(initWithTwoArgs:other:)"
104-
// CHECK-LABEL: define linkonce_odr hidden {{.+}} @"$sSo3BarC2__ABSgs5Int32V_tcfcTO"
105-
// CHECK: @"\01L_selector(init:)"
106-
107107
_ = __PrivAnonymousA
108108
_ = __E0PrivA
109109
_ = __PrivE1A as __PrivE1

test/ClangImporter/objc_ir.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,10 @@ func initCallToAllocInit(i i: CInt) {
5050
// CHECK-LABEL: linkonce_odr hidden {{.*}} @"$sSo1BC3intABSgs5Int32V_tcfC"
5151
// CHECK: call [[OPAQUE:%.*]]* @objc_allocWithZone
5252

53+
// CHECK: linkonce_odr hidden {{.*}} @"$sSo1BC3intABSgs5Int32V_tcfcTO"
54+
// CHECK: load i8*, i8** @"\01L_selector(initWithInt:)"
55+
// CHECK: call [[OPAQUE:%.*]]* bitcast (void ()* @objc_msgSend
56+
5357
// Indexed subscripting
5458
// CHECK-LABEL: define hidden swiftcc void @"$s7objc_ir19indexedSubscripting1b3idx1aySo1BC_SiSo1ACtF"
5559
func indexedSubscripting(b b: B, idx: Int, a: A) {
@@ -352,10 +356,6 @@ func testBlocksWithGenerics(hba: HasBlockArray) -> Any {
352356
return hba.blockArray
353357
}
354358

355-
// CHECK: linkonce_odr hidden {{.*}} @"$sSo1BC3intABSgs5Int32V_tcfcTO"
356-
// CHECK: load i8*, i8** @"\01L_selector(initWithInt:)"
357-
// CHECK: call [[OPAQUE:%.*]]* bitcast (void ()* @objc_msgSend
358-
359359

360360
// CHECK: attributes [[NOUNWIND]] = { nounwind }
361361

test/SILGen/objc_currying.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,3 +210,17 @@ func curry_returnsInnerPointer_AnyObject(_ x: AnyObject) -> () -> UnsafeMutableR
210210
return x.returnsInnerPointer!
211211
}
212212

213+
// Make sure we don't crash if the same imported declaration is referenced
214+
// normally and as a curry thunk
215+
216+
// CHECK-LABEL: sil hidden [ossa] @$s13objc_currying17curry_initializerSo5GizmoCSgSicyF : $@convention(thin) () -> @owned @callee_guaranteed (Int) -> @owned Optional<Gizmo> {
217+
func curry_initializer() -> (Int) -> Gizmo? {
218+
_ = Gizmo.init(bellsOn: 10)
219+
return Gizmo.init(bellsOn:)
220+
}
221+
222+
// CHECK-LABEL: sil shared [serializable] [ossa] @$sSo5GizmoC7bellsOnABSgSi_tcfC : $@convention(method) (Int, @thick Gizmo.Type) -> @owned Optional<Gizmo> {
223+
224+
// CHECK-LABEL: sil shared [serializable] [thunk] [ossa] @$sSo5GizmoC7bellsOnABSgSi_tcfcTO : $@convention(method) (Int, @owned Gizmo) -> @owned Optional<Gizmo> {
225+
226+
// CHECK-LABEL: sil shared [serializable] [thunk] [ossa] @$sSo5GizmoC7bellsOnABSgSi_tcfCTcTd : $@convention(thin) (@thick Gizmo.Type) -> @owned @callee_guaranteed (Int) -> @owned Optional<Gizmo> {

test/SILGen/objc_imported_generic.swift

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,9 @@ public func arraysOfGenericParam<T: AnyObject>(y: Array<T>) {
9797
x.propertyArrayOfThings = y
9898
}
9999

100+
// CHECK-LABEL: sil shared [serializable] [thunk] [ossa] @$sSo12GenericClassC13arrayOfThingsAByxGSgSayxG_tcfcTO
101+
// CHECK: objc_method {{%.*}} : $GenericClass<T>, #GenericClass.init!initializer.1.foreign {{.*}}, $@convention(objc_method) @pseudogeneric <τ_0_0 where τ_0_0 : AnyObject> (NSArray, @owned GenericClass<τ_0_0>) -> @owned Optional<GenericClass<τ_0_0>>
102+
100103
// CHECK-LABEL: sil private [ossa] @$s21objc_imported_generic0C4FuncyyxmRlzClFyycfU_ : $@convention(thin) <V where V : AnyObject> () -> () {
101104
// CHECK: [[META:%.*]] = metatype $@thick GenericClass<V>.Type
102105
// CHECK: [[INIT:%.*]] = function_ref @$sSo12GenericClassCAByxGycfC : $@convention(method) <τ_0_0 where τ_0_0 : AnyObject> (@thick GenericClass<τ_0_0>.Type) -> @owned GenericClass<τ_0_0>
@@ -115,11 +118,6 @@ func configureWithoutOptions() {
115118
_ = GenericClass<NSObject>(options: nil)
116119
}
117120

118-
// This gets emitted down here for some reason
119-
120-
// CHECK-LABEL: sil shared [serializable] [thunk] [ossa] @$sSo12GenericClassC13arrayOfThingsAByxGSgSayxG_tcfcTO
121-
// CHECK: objc_method {{%.*}} : $GenericClass<T>, #GenericClass.init!initializer.1.foreign {{.*}}, $@convention(objc_method) @pseudogeneric <τ_0_0 where τ_0_0 : AnyObject> (NSArray, @owned GenericClass<τ_0_0>) -> @owned Optional<GenericClass<τ_0_0>>
122-
123121
// foreign to native thunk for init(options:), uses GenericOption : Hashable
124122
// conformance
125123

0 commit comments

Comments
 (0)