Skip to content

Commit 94f2394

Browse files
authored
Add a -experimental-hermetic-seal-at-link flag that triggers aggressive LTO-based dead-stripping (VFE, WME, conditional runtime records, internalization) (#39793)
1 parent 1ab88ed commit 94f2394

File tree

6 files changed

+135
-0
lines changed

6 files changed

+135
-0
lines changed

include/swift/AST/DiagnosticsDriver.def

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,14 @@ ERROR(error_darwin_only_supports_libcxx, none,
166166
"The only C++ standard library supported on Apple platforms is libc++",
167167
())
168168

169+
ERROR(error_hermetic_seal_cannot_have_library_evolution, none,
170+
"Cannot use -experimental-hermetic-seal-at-link with -enable-library-evolution",
171+
())
172+
173+
ERROR(error_hermetic_seal_requires_lto, none,
174+
"-experimental-hermetic-seal-at-link requires -lto=llvm-full or -lto=llvm-thin",
175+
())
176+
169177
WARNING(warn_drv_darwin_sdk_invalid_settings, none,
170178
"SDK settings were ignored because 'SDKSettings.json' could not be parsed",
171179
())

include/swift/Option/Options.td

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -620,6 +620,11 @@ def requirement_machine_protocol_signatures_EQ : Joined<["-"], "requirement-mach
620620
Flags<[FrontendOption]>,
621621
HelpText<"Control usage of experimental protocol requirement signature minimization: 'on', 'off', or 'verify'">;
622622

623+
def experimental_hermetic_seal_at_link:
624+
Flag<["-"], "experimental-hermetic-seal-at-link">,
625+
Flags<[FrontendOption, HelpHidden]>,
626+
HelpText<"Library code can assume that all clients are visible at linktime, and aggressively strip unused code">;
627+
623628
// Diagnostic control options
624629
def suppress_warnings : Flag<["-"], "suppress-warnings">,
625630
Flags<[FrontendOption]>,

lib/Driver/Driver.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,28 @@ static void validateSearchPathArgs(DiagnosticEngine &diags,
283283
}
284284
}
285285

286+
static void validateLinkArgs(DiagnosticEngine &diags, const ArgList &args) {
287+
if (args.hasArg(options::OPT_experimental_hermetic_seal_at_link)) {
288+
if (args.hasArg(options::OPT_enable_library_evolution)) {
289+
diags.diagnose(SourceLoc(),
290+
diag::error_hermetic_seal_cannot_have_library_evolution);
291+
}
292+
293+
bool ltoOk = false;
294+
if (const Arg *A = args.getLastArg(options::OPT_lto)) {
295+
StringRef name = A->getValue();
296+
if (name == "llvm-thin" || name == "llvm-full") {
297+
ltoOk = true;
298+
}
299+
}
300+
301+
if (!ltoOk) {
302+
diags.diagnose(SourceLoc(),
303+
diag::error_hermetic_seal_requires_lto);
304+
}
305+
}
306+
}
307+
286308
/// Perform miscellaneous early validation of arguments.
287309
static void validateArgs(DiagnosticEngine &diags, const ArgList &args,
288310
const llvm::Triple &T) {
@@ -294,6 +316,7 @@ static void validateArgs(DiagnosticEngine &diags, const ArgList &args,
294316
validateCompilationConditionArgs(diags, args);
295317
validateSearchPathArgs(diags, args);
296318
validateVerifyIncrementalDependencyArgs(diags, args);
319+
validateLinkArgs(diags, args);
297320
}
298321

299322
std::unique_ptr<ToolChain>

lib/Driver/ToolChains.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
#include "ToolChains.h"
1414

15+
#include "swift/AST/DiagnosticsDriver.h"
1516
#include "swift/Basic/Dwarf.h"
1617
#include "swift/Basic/LLVM.h"
1718
#include "swift/Basic/Platform.h"
@@ -198,6 +199,13 @@ void ToolChain::addCommonFrontendArgs(const OutputInfo &OI,
198199
inputArgs.MakeArgString(Twine("-stdlib=") + arg->getValue()));
199200
}
200201

202+
if (inputArgs.hasArg(options::OPT_experimental_hermetic_seal_at_link)) {
203+
arguments.push_back("-enable-llvm-vfe");
204+
arguments.push_back("-enable-llvm-wme");
205+
arguments.push_back("-conditional-runtime-records");
206+
arguments.push_back("-internalize-at-link");
207+
}
208+
201209
// Handle the CPU and its preferences.
202210
inputArgs.AddLastArg(arguments, options::OPT_target_cpu);
203211

test/Driver/hermetic-seal.swift

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// RUN: %swiftc_driver -driver-print-jobs -target x86_64-apple-macosx10.9 %s \
2+
// RUN: -experimental-hermetic-seal-at-link -lto=llvm-full 2>&1 | %FileCheck %s
3+
4+
// CHECK: swift
5+
// CHECK: -enable-llvm-vfe
6+
// CHECK: -enable-llvm-wme
7+
// CHECK: -conditional-runtime-records
8+
// CHECK: -internalize-at-link
9+
// CHECK: -lto=llvm-full
10+
11+
// RUN: not %swiftc_driver -driver-print-jobs -target x86_64-apple-macosx10.9 %s \
12+
// RUN: -experimental-hermetic-seal-at-link -enable-library-evolution 2>&1 | %FileCheck %s --check-prefix CHECK-LE
13+
14+
// CHECK-LE: error: Cannot use -experimental-hermetic-seal-at-link with -enable-library-evolution
15+
16+
// RUN: not %swiftc_driver -driver-print-jobs -target x86_64-apple-macosx10.9 %s \
17+
// RUN: -experimental-hermetic-seal-at-link 2>&1 | %FileCheck %s --check-prefix CHECK-NOLTO
18+
19+
// CHECK-NOLTO: error: -experimental-hermetic-seal-at-link requires -lto=llvm-full or -lto=llvm-thin

test/IRGen/hermetic-seal-exec.swift

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
// End-to-end test of -experimental-hermetic-seal-at-link flag.
2+
3+
// RUN: %empty-directory(%t)
4+
5+
// (1) Build library swiftmodule
6+
// RUN: %target-build-swift %s -DLIBRARY -module-name Library -experimental-hermetic-seal-at-link -lto=llvm-full %lto_flags \
7+
// RUN: -Xfrontend -disable-reflection-metadata -Xfrontend -disable-reflection-names -Xfrontend -disable-objc-interop \
8+
// RUN: -emit-library -static -o %t/libLibrary.a \
9+
// RUN: -emit-module -emit-module-path %t/Library.swiftmodule
10+
11+
// (2) Check that libLibrary.a does actually provide all its public interfaces
12+
// RUN: %llvm-nm %t/libLibrary.a | %FileCheck %s --check-prefix CHECK-NM-LIB
13+
14+
// (3) Build client
15+
// RUN: %target-build-swift %s -DCLIENT -parse-as-library -module-name Main -experimental-hermetic-seal-at-link -lto=llvm-full %lto_flags \
16+
// RUN: -Xfrontend -disable-reflection-metadata -Xfrontend -disable-reflection-names -Xfrontend -disable-objc-interop \
17+
// RUN: -I%t -L%t -lLibrary -o %t/main
18+
19+
// (4) Check that unused symbols are not present in final executable
20+
// RUN: %llvm-nm %t/main | %FileCheck %s --check-prefix CHECK-NM-EXEC
21+
22+
// (5) Execute
23+
// RUN: %target-run %t/main | %FileCheck %s
24+
25+
// REQUIRES: executable_test
26+
27+
// Test disabled until LLVM GlobalDCE supports conditional references.
28+
// REQUIRES: rdar81868900
29+
30+
#if LIBRARY
31+
32+
// The only symbol that's actually used by client code
33+
public func used_func() {
34+
print("used_func")
35+
}
36+
37+
public class MyUnusedClass {}
38+
public class MyUnusedSubClass: MyUnusedClass {}
39+
public protocol MyUnusedProtocol {}
40+
public struct MyUnusedStruct {}
41+
public struct MyUnusedStruct2: MyUnusedProtocol {}
42+
public enum MyUnusedEnum {}
43+
public func MyUnusedFunc() {}
44+
45+
// (2) In libLibrary.a, all exported symbols are present...
46+
// CHECK-NM-LIB-DAG: MyUnusedClass
47+
// CHECK-NM-LIB-DAG: MyUnusedSubClass
48+
// CHECK-NM-LIB-DAG: MyUnusedProtocol
49+
// CHECK-NM-LIB-DAG: MyUnusedStruct
50+
// CHECK-NM-LIB-DAG: MyUnusedStruct2
51+
// CHECK-NM-LIB-DAG: MyUnusedEnum
52+
// CHECK-NM-LIB-DAG: MyUnusedFunc
53+
54+
// (4) ... but after linking the main executable, they are removed.
55+
// CHECK-NM-EXEC-NOT: MyUnused
56+
57+
#endif // LIBRARY
58+
59+
#if CLIENT
60+
61+
import Library
62+
63+
@_cdecl("main")
64+
func main() -> Int32 {
65+
used_func()
66+
print("Done")
67+
// CHECK: used_func
68+
// CHECK-NEXT: Done
69+
return 0
70+
}
71+
72+
#endif // CLIENT

0 commit comments

Comments
 (0)