Skip to content

Commit db6e3ed

Browse files
committed
Merge pull request swiftlang#69694 from apple/revert-69689-revert-69609-AlwaysEmitConformanceMetadataPreservation
Revert "Revert "Add mandatory SIL pass implementing '@_alwaysEmitConformanceMetadata' protocol attribute""
1 parent bef8b61 commit db6e3ed

File tree

7 files changed

+150
-0
lines changed

7 files changed

+150
-0
lines changed

include/swift/SILOptimizer/PassManager/Passes.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,8 @@ PASS(AccessEnforcementSelection, "access-enforcement-selection",
9898
"Access Enforcement Selection")
9999
PASS(AccessEnforcementWMO, "access-enforcement-wmo",
100100
"Access Enforcement Whole Module Optimization")
101+
PASS(AlwaysEmitConformanceMetadataPreservation, "preserve-always-emit-conformance-metadata",
102+
"Mark conformances to @_alwaysEmitConformanceMetadata protocols as externally visible")
101103
PASS(CrossModuleOptimization, "cmo",
102104
"Perform cross-module optimization")
103105
PASS(AccessSummaryDumper, "access-summary-dump",

lib/IRGen/IRGenModule.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1354,6 +1354,11 @@ bool IRGenerator::canEmitWitnessTableLazily(SILWitnessTable *wt) {
13541354
if (wt->getLinkage() == SILLinkage::Shared)
13551355
return true;
13561356

1357+
// Check if this type is set to be explicitly externally visible
1358+
NominalTypeDecl *ConformingTy = wt->getConformingNominal();
1359+
if (PrimaryIGM->getSILModule().isExternallyVisibleDecl(ConformingTy))
1360+
return false;
1361+
13571362
switch (wt->getConformingNominal()->getEffectiveAccess()) {
13581363
case AccessLevel::Private:
13591364
case AccessLevel::FilePrivate:
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
//===--- AlwaysEmitConformanceMetadataPreservation.cpp -------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
///
13+
/// Some frameworks may rely on conformances to protocols they provide
14+
/// to be present in binary product they are compiled into even if
15+
/// such conformances are not otherwise referenced in user code.
16+
/// Such conformances may then, for example, be queried and used by the
17+
/// runtime.
18+
///
19+
/// The developer may not ever explicitly reference or instantiate this type in
20+
/// their code, as it is effectively defining an XPC endpoint. However, when
21+
/// optimizations are enabled, the type may be stripped from the binary as it is
22+
/// never referenced. `@_alwaysEmitConformanceMetadata` can be used to mark
23+
/// a protocol to ensure that its conformances are always marked as externally
24+
/// visible even if not `public` to ensure they do not get optimized away.
25+
/// This mandatory pass makes it so.
26+
///
27+
//===----------------------------------------------------------------------===//
28+
29+
#include "swift/AST/Attr.h"
30+
#define DEBUG_TYPE "always-emit-conformance-metadata-preservation"
31+
#include "swift/AST/ASTWalker.h"
32+
#include "swift/AST/NameLookup.h"
33+
#include "swift/AST/ProtocolConformance.h"
34+
#include "swift/AST/SourceFile.h"
35+
#include "swift/SIL/SILModule.h"
36+
#include "swift/SILOptimizer/PassManager/Passes.h"
37+
#include "swift/SILOptimizer/PassManager/Transforms.h"
38+
39+
using namespace swift;
40+
41+
namespace {
42+
43+
/// A helper class to collect all nominal type declarations that conform to
44+
/// `@_alwaysEmitConformanceMetadata` protocols.
45+
class AlwaysEmitMetadataConformanceCollector : public ASTWalker {
46+
std::vector<NominalTypeDecl *> &AlwaysEmitMetadataConformanceDecls;
47+
48+
public:
49+
AlwaysEmitMetadataConformanceCollector(
50+
std::vector<NominalTypeDecl *> &AlwaysEmitMetadataConformanceDecls)
51+
: AlwaysEmitMetadataConformanceDecls(AlwaysEmitMetadataConformanceDecls) {
52+
}
53+
54+
PreWalkAction walkToDeclPre(Decl *D) override {
55+
auto hasAlwaysEmitMetadataConformance =
56+
[&](llvm::PointerUnion<const TypeDecl *, const ExtensionDecl *> Decl) {
57+
bool anyObject = false;
58+
InvertibleProtocolSet Inverses;
59+
for (const auto &found :
60+
getDirectlyInheritedNominalTypeDecls(Decl, Inverses, anyObject))
61+
if (auto Protocol = dyn_cast<ProtocolDecl>(found.Item))
62+
if (Protocol->getAttrs()
63+
.hasAttribute<AlwaysEmitConformanceMetadataAttr>())
64+
return true;
65+
return false;
66+
};
67+
68+
if (auto *NTD = dyn_cast<NominalTypeDecl>(D)) {
69+
if (hasAlwaysEmitMetadataConformance(NTD))
70+
AlwaysEmitMetadataConformanceDecls.push_back(NTD);
71+
} else if (auto *ETD = dyn_cast<ExtensionDecl>(D)) {
72+
if (hasAlwaysEmitMetadataConformance(ETD))
73+
AlwaysEmitMetadataConformanceDecls.push_back(ETD->getExtendedNominal());
74+
}
75+
76+
return Action::Continue();
77+
}
78+
};
79+
80+
class AlwaysEmitConformanceMetadataPreservation : public SILModuleTransform {
81+
void run() override {
82+
auto &M = *getModule();
83+
std::vector<NominalTypeDecl *> AlwaysEmitMetadataConformanceDecls;
84+
AlwaysEmitMetadataConformanceCollector Walker(
85+
AlwaysEmitMetadataConformanceDecls);
86+
87+
SmallVector<Decl *> TopLevelDecls;
88+
if (M.getSwiftModule()->isMainModule()) {
89+
if (M.isWholeModule()) {
90+
for (const auto File : M.getSwiftModule()->getFiles())
91+
File->getTopLevelDecls(TopLevelDecls);
92+
} else {
93+
for (const auto Primary : M.getSwiftModule()->getPrimarySourceFiles())
94+
Primary->getTopLevelDecls(TopLevelDecls);
95+
}
96+
}
97+
for (auto *TLD : TopLevelDecls)
98+
TLD->walk(Walker);
99+
100+
for (auto &NTD : AlwaysEmitMetadataConformanceDecls)
101+
M.addExternallyVisibleDecl(NTD);
102+
}
103+
};
104+
} // end anonymous namespace
105+
106+
SILTransform *swift::createAlwaysEmitConformanceMetadataPreservation() {
107+
return new AlwaysEmitConformanceMetadataPreservation();
108+
}

lib/SILOptimizer/Mandatory/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
target_sources(swiftSILOptimizer PRIVATE
22
AccessEnforcementSelection.cpp
33
AccessMarkerElimination.cpp
4+
AlwaysEmitConformanceMetadataPreservation.cpp
45
AddressLowering.cpp
56
CapturePromotion.cpp
67
ClosureLifetimeFixup.cpp

lib/SILOptimizer/PassManager/PassPipeline.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -900,6 +900,7 @@ SILPassPipelinePlan::getLoweringPassPipeline(const SILOptions &Options) {
900900
P.startPipeline("Lowering");
901901
P.addLowerHopToActor(); // FIXME: earlier for more opportunities?
902902
P.addOwnershipModelEliminator();
903+
P.addAlwaysEmitConformanceMetadataPreservation();
903904
P.addIRGenPrepare();
904905

905906
return P;
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
@_alwaysEmitConformanceMetadata
2+
public protocol TestEntity {}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// REQUIRES: objc_interop
2+
// RUN: %empty-directory(%t)
3+
// RUN: %empty-directory(%t/includes)
4+
5+
// Build support Protocols module
6+
// RUN: %target-build-swift %S/Inputs/PreservedConformanceProtocols.swift -parse-as-library -emit-module -emit-library -module-name PreservedConformanceProtocols -o %t/includes/PreservedConformanceProtocols.o
7+
8+
// Build the test into a binary
9+
// RUN: %target-build-swift %s -parse-as-library -emit-module -emit-library -module-name PreservedConformances -O -whole-module-optimization -I %t/includes -o %t/PreservedConformances -Xlinker %t/includes/PreservedConformanceProtocols.o
10+
11+
// RUN: %target-swift-reflection-dump %t/PreservedConformances | %FileCheck %s
12+
13+
import PreservedConformanceProtocols
14+
15+
struct internalTestEntity : TestEntity {
16+
struct internalNestedTestEntity : TestEntity {}
17+
}
18+
private struct privateTestEntity : TestEntity {
19+
private struct privateNestedTestEntity : TestEntity {}
20+
}
21+
fileprivate struct filePrivateTestEntity : TestEntity {}
22+
public struct publicTestEntity : TestEntity {}
23+
24+
// CHECK: CONFORMANCES:
25+
// CHECK: =============
26+
// CHECK-DAG: 21PreservedConformances16publicTestEntityV (PreservedConformances.publicTestEntity) : PreservedConformanceProtocols.TestEntity
27+
// CHECK-DAG: 21PreservedConformances21filePrivateTestEntity5${{[0-9a-f]+}}LLV (PreservedConformances.(filePrivateTestEntity in ${{[0-9a-f]+}})) : PreservedConformanceProtocols.TestEntity
28+
// CHECK-DAG: 21PreservedConformances17privateTestEntity5${{[0-9a-f]+}}LLV (PreservedConformances.(privateTestEntity in ${{[0-9a-f]+}})) : PreservedConformanceProtocols.TestEntity
29+
// CHECK-DAG: 21PreservedConformances17privateTestEntity5${{[0-9a-f]+}}LLV0c6NesteddE0V (PreservedConformances.(privateTestEntity in ${{[0-9a-f]+}}).privateNestedTestEntity) : PreservedConformanceProtocols.TestEntity
30+
// CHECK-DAG: 21PreservedConformances18internalTestEntityV (PreservedConformances.internalTestEntity) : PreservedConformanceProtocols.TestEntity
31+
// CHECK-DAG: 21PreservedConformances18internalTestEntityV0c6NesteddE0V (PreservedConformances.internalTestEntity.internalNestedTestEntity) : PreservedConformanceProtocols.TestEntity

0 commit comments

Comments
 (0)