Skip to content

Commit 9a1a2c3

Browse files
authored
Merge pull request #64644 from tshortli/strip-unavailable-code
Introduce `-unavailable-decl-optimization`
2 parents d9b7ddc + 98b4e79 commit 9a1a2c3

File tree

14 files changed

+512
-4
lines changed

14 files changed

+512
-4
lines changed

include/swift/Basic/LangOptions.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,22 @@ namespace swift {
105105
TaskToThread,
106106
};
107107

108+
/// Describes the code size optimization behavior for code associated with
109+
/// declarations that are marked unavailable.
110+
enum class UnavailableDeclOptimization : uint8_t {
111+
/// No optimization. Unavailable declarations will contribute to the
112+
/// resulting binary by default in this mode.
113+
None,
114+
115+
/// Avoid generating any code for unavailable declarations.
116+
///
117+
/// NOTE: This optimization can be ABI breaking for a library evolution
118+
/// enabled module because existing client binaries built with a
119+
/// pre-Swift 5.9 compiler may depend on linkable symbols associated with
120+
/// unavailable declarations.
121+
Complete,
122+
};
123+
108124
/// A collection of options that affect the language dialect and
109125
/// provide compiler debugging facilities.
110126
class LangOptions final {
@@ -171,6 +187,10 @@ namespace swift {
171187
/// Disable API availability checking.
172188
bool DisableAvailabilityChecking = false;
173189

190+
/// Optimization mode for unavailable declarations.
191+
UnavailableDeclOptimization UnavailableDeclOptimizationMode =
192+
UnavailableDeclOptimization::None;
193+
174194
/// Causes the compiler to use weak linkage for symbols belonging to
175195
/// declarations introduced at the deployment target.
176196
bool WeakLinkAtTarget = false;

include/swift/Option/Options.td

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -490,6 +490,13 @@ def check_api_availability_only : Flag<["-"], "check-api-availability-only">,
490490
Flags<[HelpHidden, FrontendOption, NoInteractiveOption]>,
491491
HelpText<"Deprecated, has no effect">;
492492

493+
def unavailable_decl_optimization_EQ : Joined<["-"], "unavailable-decl-optimization=">,
494+
MetaVarName<"<complete,none>">,
495+
Flags<[FrontendOption, NoInteractiveOption]>,
496+
HelpText<"Specify the optimization mode for unavailable declarations. The "
497+
"value may be 'none' (no optimization) or 'complete' (code is not "
498+
"generated at all unavailable declarations)">;
499+
493500
def library_level : Separate<["-"], "library-level">,
494501
MetaVarName<"<level>">,
495502
Flags<[HelpHidden, FrontendOption, ModuleInterfaceOption]>,

include/swift/SIL/SILModule.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1078,8 +1078,22 @@ namespace Lowering {
10781078
/// Determine whether the given class will be allocated/deallocated using the
10791079
/// Objective-C runtime, i.e., +alloc and -dealloc.
10801080
LLVM_LIBRARY_VISIBILITY bool usesObjCAllocator(ClassDecl *theClass);
1081+
1082+
/// Returns true if SIL/IR lowering for the given declaration should be skipped.
1083+
/// A declaration may not require lowering if, for example, it is annotated as
1084+
/// unavailable and optimization settings allow it to be omitted.
1085+
LLVM_LIBRARY_VISIBILITY bool shouldSkipLowering(Decl *D);
10811086
} // namespace Lowering
10821087

1088+
/// Apply the given function to each ABI member of \c D skipping the members
1089+
/// that should be skipped according to \c shouldSkipLowering()
1090+
template <typename F>
1091+
void forEachMemberToLower(IterableDeclContext *D, F &&f) {
1092+
for (auto *member : D->getABIMembers()) {
1093+
if (!Lowering::shouldSkipLowering(member))
1094+
f(member);
1095+
}
1096+
}
10831097
} // namespace swift
10841098

10851099
#endif

include/swift/SIL/SILVTableVisitor.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,8 +141,9 @@ template <class T> class SILVTableVisitor {
141141
if (!theClass->hasKnownSwiftImplementation())
142142
return;
143143

144-
for (auto member : theClass->getABIMembers())
144+
forEachMemberToLower(theClass, [&](Decl *member) {
145145
maybeAddMember(member);
146+
});
146147
}
147148
};
148149

lib/AST/Availability.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,9 @@ AvailabilityInference::parentDeclForInferredAvailability(const Decl *D) {
181181
return NTD;
182182
}
183183

184+
if (auto *PBD = dyn_cast<PatternBindingDecl>(D))
185+
return PBD->getAnchoringVarDecl(0);
186+
184187
// Clang decls may be inaccurately parented rdar://53956555
185188
if (D->hasClangNode())
186189
return nullptr;

lib/Frontend/CompilerInvocation.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -552,6 +552,20 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args,
552552
Diags.diagnose(SourceLoc(), diag::warn_flag_deprecated,
553553
"-check-api-availability-only");
554554

555+
if (const Arg *A = Args.getLastArg(OPT_unavailable_decl_optimization_EQ)) {
556+
auto value =
557+
llvm::StringSwitch<Optional<UnavailableDeclOptimization>>(A->getValue())
558+
.Case("none", UnavailableDeclOptimization::None)
559+
.Case("complete", UnavailableDeclOptimization::Complete)
560+
.Default(None);
561+
562+
if (value)
563+
Opts.UnavailableDeclOptimizationMode = *value;
564+
else
565+
Diags.diagnose(SourceLoc(), diag::error_invalid_arg_value,
566+
A->getAsString(Args), A->getValue());
567+
}
568+
555569
Opts.WeakLinkAtTarget |= Args.hasArg(OPT_weak_link_at_target);
556570

557571
if (auto A = Args.getLastArg(OPT_enable_conformance_availability_errors,

lib/IRGen/GenDecl.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2458,9 +2458,13 @@ void swift::irgen::disableAddressSanitizer(IRGenModule &IGM, llvm::GlobalVariabl
24582458

24592459
/// Emit a global declaration.
24602460
void IRGenModule::emitGlobalDecl(Decl *D) {
2461+
if (Lowering::shouldSkipLowering(D))
2462+
return;
2463+
24612464
D->visitAuxiliaryDecls([&](Decl *decl) {
24622465
emitGlobalDecl(decl);
24632466
});
2467+
24642468
switch (D->getKind()) {
24652469
case DeclKind::Extension:
24662470
return emitExtension(cast<ExtensionDecl>(D));

lib/IRGen/TBDGen.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,9 @@ void TBDGenVisitor::addSymbol(StringRef name, SymbolSource source,
414414
}
415415

416416
bool TBDGenVisitor::willVisitDecl(Decl *D) {
417+
if (Lowering::shouldSkipLowering(D))
418+
return false;
419+
417420
// A @_silgen_name("...") function without a body only exists to
418421
// forward-declare a symbol from another library.
419422
if (auto AFD = dyn_cast<AbstractFunctionDecl>(D))

lib/SIL/IR/SILModule.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -955,3 +955,13 @@ bool Lowering::usesObjCAllocator(ClassDecl *theClass) {
955955
// allocation methods because they may have been overridden.
956956
return theClass->getObjectModel() == ReferenceCounting::ObjC;
957957
}
958+
959+
bool Lowering::shouldSkipLowering(Decl *D) {
960+
if (D->getASTContext().LangOpts.UnavailableDeclOptimizationMode !=
961+
UnavailableDeclOptimization::Complete)
962+
return false;
963+
964+
// Unavailable declarations should be skipped if
965+
// -unavailable-decl-optimization=complete is specified.
966+
return D->getSemanticUnavailableAttr() != None;
967+
}

lib/SILGen/SILGen.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -770,6 +770,13 @@ bool SILGenModule::hasFunction(SILDeclRef constant) {
770770
return emittedFunctions.count(constant);
771771
}
772772

773+
void SILGenModule::visit(Decl *D) {
774+
if (Lowering::shouldSkipLowering(D))
775+
return;
776+
777+
ASTVisitor::visit(D);
778+
}
779+
773780
void SILGenModule::visitFuncDecl(FuncDecl *fd) { emitFunction(fd); }
774781

775782
void SILGenModule::emitFunctionDefinition(SILDeclRef constant, SILFunction *f) {

lib/SILGen/SILGen.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,8 @@ class LLVM_LIBRARY_VISIBILITY SILGenModule : public ASTVisitor<SILGenModule> {
266266
// Visitors for top-level forms
267267
//===--------------------------------------------------------------------===//
268268

269+
void visit(Decl *D);
270+
269271
// These are either not allowed at global scope or don't require
270272
// code emission.
271273
void visitImportDecl(ImportDecl *d) {}

lib/SILGen/SILGenType.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1085,8 +1085,9 @@ class SILGenType : public TypeMemberVisitor<SILGenType> {
10851085
void emitType() {
10861086
SGM.emitLazyConformancesForType(theType);
10871087

1088-
for (Decl *member : theType->getABIMembers())
1088+
forEachMemberToLower(theType, [&](Decl *member) {
10891089
visit(member);
1090+
});
10901091

10911092
// Build a vtable if this is a class.
10921093
if (auto theClass = dyn_cast<ClassDecl>(theType)) {
@@ -1252,9 +1253,10 @@ class SILGenExtension : public TypeMemberVisitor<SILGenExtension> {
12521253
// @_objcImplementation extension, but we don't actually need to do any of
12531254
// the stuff that it currently does.
12541255

1255-
for (Decl *member : e->getABIMembers())
1256+
forEachMemberToLower(e, [&](Decl *member) {
12561257
visit(member);
1257-
1258+
});
1259+
12581260
// If this is a main-interface @_objcImplementation extension and the class
12591261
// has a synthesized destructor, emit it now.
12601262
if (auto cd = dyn_cast_or_null<ClassDecl>(e->getImplementedObjCDecl())) {
Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
// RUN: %target-swift-frontend -parse-as-library -module-name Test -validate-tbd-against-ir=missing %s -emit-ir | %FileCheck %s --check-prefixes=CHECK,CHECK-NO-STRIP
2+
3+
// RUN: %target-swift-frontend -parse-as-library -module-name Test -validate-tbd-against-ir=missing -unavailable-decl-optimization=complete %s -emit-ir | %FileCheck %s --check-prefixes=CHECK,CHECK-STRIP
4+
5+
// CHECK-NO-STRIP: s4Test14globalConstantSbvp
6+
// CHECK-NO-STRIP: s4Test14globalConstantSbvau
7+
// CHECK-STRIP-NOT: s4Test14globalConstantSbvp
8+
// CHECK-STRIP-NOT: s4Test14globalConstantSbvau
9+
@available(*, unavailable)
10+
public let globalConstant = true
11+
12+
// CHECK-NO-STRIP: s4Test15unavailableFuncyyF
13+
// CHECK-STRIP-NOT: s4Test15unavailableFuncyyF
14+
@available(*, unavailable)
15+
public func unavailableFunc() {}
16+
17+
@available(*, unavailable)
18+
public struct UnavailableStruct<T> {
19+
// CHECK-NO-STRIP: s4Test17UnavailableStructV8propertyxvg
20+
// CHECK-NO-STRIP: s4Test17UnavailableStructV8propertyxvs
21+
// CHECK-NO-STRIP: s4Test17UnavailableStructV8propertyxvM
22+
// CHECK-STRIP-NOT: s4Test17UnavailableStructV8propertyxvg
23+
// CHECK-STRIP-NOT: s4Test17UnavailableStructV8propertyxvs
24+
// CHECK-STRIP-NOT: s4Test17UnavailableStructV8propertyxvM
25+
public var property: T
26+
27+
// CHECK-NO-STRIP: s4Test17UnavailableStructVyACyxGxcfC
28+
// CHECK-NO-STRIP: s4Test17UnavailableStructVMa
29+
// CHECK-STRIP-NOT: s4Test17UnavailableStructVyACyxGxcfC
30+
// CHECK-STRIP-NOT: s4Test17UnavailableStructVMa
31+
public init(_ t: T) {
32+
self.property = t
33+
}
34+
35+
// CHECK-NO-STRIP: s4Test17UnavailableStructV6methodyyF
36+
// CHECK-STRIP-NOT: s4Test17UnavailableStructV6methodyyF
37+
public func method() {}
38+
}
39+
40+
@available(*, unavailable)
41+
extension UnavailableStruct {
42+
// CHECK-NO-STRIP: s4Test17UnavailableStructV15extensionMethodyyF
43+
// CHECK-STRIP-NOT: s4Test17UnavailableStructV15extensionMethodyyF
44+
public func extensionMethod() {}
45+
}
46+
47+
@available(*, unavailable)
48+
public enum UnavailableEnum {
49+
case a, b
50+
51+
// CHECK-NO-STRIP: s4Test15UnavailableEnumO6methodyyF
52+
// CHECK-STRIP-NOT: s4Test15UnavailableEnumO6methodyyF
53+
public func method() {}
54+
55+
// CHECK-NO-STRIP: s4Test15UnavailableEnumO21__derived_enum_equalsySbAC_ACtFZ
56+
// CHECK-NO-STRIP: s4Test15UnavailableEnumO4hash4intoys6HasherVz_tF
57+
// CHECK-NO-STRIP: s4Test15UnavailableEnumO9hashValueSivg
58+
// CHECK-STRIP-NOT: s4Test15UnavailableEnumO21__derived_enum_equalsySbAC_ACtFZ
59+
// CHECK-STRIP-NOT: s4Test15UnavailableEnumO4hash4intoys6HasherVz_tF
60+
// CHECK-STRIP-NOT: s4Test15UnavailableEnumO9hashValueSivg
61+
}
62+
63+
@available(*, unavailable)
64+
public class UnavailableClass<T> {
65+
// CHECK-NO-STRIP: s4Test16UnavailableClassC8propertyxvg
66+
// CHECK-NO-STRIP: s4Test16UnavailableClassC8propertyxvs
67+
// CHECK-NO-STRIP: s4Test16UnavailableClassC8propertyxvM
68+
// CHECK-STRIP-NOT: s4Test16UnavailableClassC8propertyxvg
69+
// CHECK-STRIP-NOT: s4Test16UnavailableClassC8propertyxvs
70+
// CHECK-STRIP-NOT: s4Test16UnavailableClassC8propertyxvM
71+
public var property: T
72+
73+
// CHECK-NO-STRIP: s4Test16UnavailableClassCyACyxGxcfC
74+
// CHECK-NO-STRIP: s4Test16UnavailableClassCyACyxGxcfc
75+
// CHECK-STRIP-NOT: s4Test16UnavailableClassCyACyxGxcfC
76+
// CHECK-STRIP-NOT: s4Test16UnavailableClassCyACyxGxcfc
77+
public init(_ t: T) {
78+
self.property = t
79+
}
80+
81+
// CHECK-NO-STRIP: s4Test16UnavailableClassCfd
82+
// CHECK-NO-STRIP: s4Test16UnavailableClassCfD
83+
// CHECK-STRIP-NOT: s4Test16UnavailableClassCfd
84+
// CHECK-STRIP-NOT: s4Test16UnavailableClassCfD
85+
deinit {}
86+
}
87+
88+
public struct S<T> {
89+
// CHECK-NO-STRIP: s4Test1SV19unavailablePropertyxvg
90+
// CHECK-NO-STRIP: s4Test1SV19unavailablePropertyxvs
91+
// CHECK-NO-STRIP: s4Test1SV19unavailablePropertyxvM
92+
// CHECK-STRIP-NOT: s4Test1SV19unavailablePropertyxvg
93+
// CHECK-STRIP-NOT: s4Test1SV19unavailablePropertyxvs
94+
// CHECK-STRIP-NOT: s4Test1SV19unavailablePropertyxvM
95+
@available(*, unavailable)
96+
public var unavailableProperty: T
97+
98+
// CHECK-NO-STRIP: s4Test1SVyACyxGxcfC
99+
// CHECK-STRIP-NOT: s4Test1SVyACyxGxcfC
100+
@available(*, unavailable)
101+
public init(_ t: T) { fatalError() }
102+
103+
// CHECK-NO-STRIP: s4Test1SV17unavailableMethodyyF
104+
// CHECK-STRIP-NOT: s4Test1SV17unavailableMethodyyF
105+
@available(*, unavailable)
106+
public func unavailableMethod() {}
107+
}
108+
109+
@available(*, unavailable)
110+
extension S {
111+
// CHECK-NO-STRIP: s4Test1SV28methodInUnavailableExtensionyyF
112+
// CHECK-STRIP-NOT: s4Test1SV28methodInUnavailableExtensionyyF
113+
public func methodInUnavailableExtension() {}
114+
}
115+
116+
public enum E {
117+
case a
118+
119+
@available(*, unavailable)
120+
case b
121+
122+
// CHECK-NO-STRIP: s4Test1EO17unavailableMethodyyF
123+
// CHECK-STRIP-NOT: s4Test1EO17unavailableMethodyyF
124+
@available(*, unavailable)
125+
public func unavailableMethod() {}
126+
}
127+
128+
public class C<T> {
129+
// CHECK-NO-STRIP: s4Test1CC19unavailablePropertyxvg
130+
// CHECK-NO-STRIP: s4Test1CC19unavailablePropertyxvs
131+
// CHECK-NO-STRIP: s4Test1CC19unavailablePropertyxvM
132+
// CHECK-STRIP-NOT: s4Test1CC19unavailablePropertyxvg
133+
// CHECK-STRIP-NOT: s4Test1CC19unavailablePropertyxvs
134+
// CHECK-STRIP-NOT: s4Test1CC19unavailablePropertyxvM
135+
@available(*, unavailable)
136+
public var unavailableProperty: T
137+
138+
// CHECK-NO-STRIP: s4Test1CCyACyxGxcfC
139+
// CHECK-NO-STRIP: s4Test1CCyACyxGxcfc
140+
// CHECK-STRIP-NOT: s4Test1CCyACyxGxcfC
141+
// CHECK-STRIP-NOT: s4Test1CCyACyxGxcfc
142+
@available(*, unavailable)
143+
public init(_ t: T) { fatalError() }
144+
145+
// CHECK: s4Test1CCfd
146+
// CHECK: s4Test1CCfD
147+
deinit {}
148+
}
149+
150+
public protocol P {
151+
func requirement()
152+
}
153+
154+
@available(*, unavailable)
155+
extension S: P {
156+
// CHECK-NO-STRIP: s4Test1SV11requirementyyF
157+
// CHECK-STRIP-NOT: s4Test1SV11requirementyyF
158+
public func requirement() {}
159+
}
160+
161+
// CHECK-NO-STRIP: s4Test29unavailableFuncWithNestedTypeyyF
162+
// CHECK-STRIP-NOT: s4Test29unavailableFuncWithNestedTypeyyF
163+
@available(*, unavailable)
164+
public func unavailableFuncWithNestedType() {
165+
struct Nested {
166+
// s4Test29unavailableFuncWithNestedTypeyyF0E0L_V6methodyyF
167+
public func method() {}
168+
}
169+
}
170+
171+
// MARK: -
172+
173+
// MARK: UnavailableEnum
174+
175+
// CHECK-NO-STRIP: s4Test15UnavailableEnumOwug
176+
// CHECK-STRIP-NOT: s4Test15UnavailableEnumOwug
177+
178+
// CHECK-NO-STRIP: s4Test15UnavailableEnumOMa
179+
// CHECK-STRIP-NOT: s4Test15UnavailableEnumOMa
180+
181+
// MARK: UnavailableClass
182+
183+
// CHECK-NO-STRIP: s4Test16UnavailableClassCMa
184+
// CHECK-STRIP-NOT: s4Test16UnavailableClassCMa
185+
186+
// MARK: E
187+
188+
// CHECK: s4Test1EOwug
189+
// CHECK: s4Test1EOMa
190+
191+
// MARK: unavailableFuncWithNestedType().Nested
192+
193+
// CHECK-NO-STRIP: s4Test29unavailableFuncWithNestedTypeyyF0E0L_VMa
194+
// CHECK-STRIP-NOT: s4Test29unavailableFuncWithNestedTypeyyF0E0L_VMa

0 commit comments

Comments
 (0)