Add a -experimental-hermetic-seal-at-link flag that triggers aggressive LTO-based dead-stripping (VFE, WME, conditional runtime records, internalization) (#39793)

This commit is contained in:
Kuba (Brecka) Mracek
2021-10-20 07:30:43 -07:00
committed by GitHub
parent 1ab88ed090
commit 94f23942db
6 changed files with 135 additions and 0 deletions

View File

@@ -166,6 +166,14 @@ ERROR(error_darwin_only_supports_libcxx, none,
"The only C++ standard library supported on Apple platforms is libc++",
())
ERROR(error_hermetic_seal_cannot_have_library_evolution, none,
"Cannot use -experimental-hermetic-seal-at-link with -enable-library-evolution",
())
ERROR(error_hermetic_seal_requires_lto, none,
"-experimental-hermetic-seal-at-link requires -lto=llvm-full or -lto=llvm-thin",
())
WARNING(warn_drv_darwin_sdk_invalid_settings, none,
"SDK settings were ignored because 'SDKSettings.json' could not be parsed",
())

View File

@@ -620,6 +620,11 @@ def requirement_machine_protocol_signatures_EQ : Joined<["-"], "requirement-mach
Flags<[FrontendOption]>,
HelpText<"Control usage of experimental protocol requirement signature minimization: 'on', 'off', or 'verify'">;
def experimental_hermetic_seal_at_link:
Flag<["-"], "experimental-hermetic-seal-at-link">,
Flags<[FrontendOption, HelpHidden]>,
HelpText<"Library code can assume that all clients are visible at linktime, and aggressively strip unused code">;
// Diagnostic control options
def suppress_warnings : Flag<["-"], "suppress-warnings">,
Flags<[FrontendOption]>,

View File

@@ -283,6 +283,28 @@ static void validateSearchPathArgs(DiagnosticEngine &diags,
}
}
static void validateLinkArgs(DiagnosticEngine &diags, const ArgList &args) {
if (args.hasArg(options::OPT_experimental_hermetic_seal_at_link)) {
if (args.hasArg(options::OPT_enable_library_evolution)) {
diags.diagnose(SourceLoc(),
diag::error_hermetic_seal_cannot_have_library_evolution);
}
bool ltoOk = false;
if (const Arg *A = args.getLastArg(options::OPT_lto)) {
StringRef name = A->getValue();
if (name == "llvm-thin" || name == "llvm-full") {
ltoOk = true;
}
}
if (!ltoOk) {
diags.diagnose(SourceLoc(),
diag::error_hermetic_seal_requires_lto);
}
}
}
/// Perform miscellaneous early validation of arguments.
static void validateArgs(DiagnosticEngine &diags, const ArgList &args,
const llvm::Triple &T) {
@@ -294,6 +316,7 @@ static void validateArgs(DiagnosticEngine &diags, const ArgList &args,
validateCompilationConditionArgs(diags, args);
validateSearchPathArgs(diags, args);
validateVerifyIncrementalDependencyArgs(diags, args);
validateLinkArgs(diags, args);
}
std::unique_ptr<ToolChain>

View File

@@ -12,6 +12,7 @@
#include "ToolChains.h"
#include "swift/AST/DiagnosticsDriver.h"
#include "swift/Basic/Dwarf.h"
#include "swift/Basic/LLVM.h"
#include "swift/Basic/Platform.h"
@@ -198,6 +199,13 @@ void ToolChain::addCommonFrontendArgs(const OutputInfo &OI,
inputArgs.MakeArgString(Twine("-stdlib=") + arg->getValue()));
}
if (inputArgs.hasArg(options::OPT_experimental_hermetic_seal_at_link)) {
arguments.push_back("-enable-llvm-vfe");
arguments.push_back("-enable-llvm-wme");
arguments.push_back("-conditional-runtime-records");
arguments.push_back("-internalize-at-link");
}
// Handle the CPU and its preferences.
inputArgs.AddLastArg(arguments, options::OPT_target_cpu);

View File

@@ -0,0 +1,19 @@
// RUN: %swiftc_driver -driver-print-jobs -target x86_64-apple-macosx10.9 %s \
// RUN: -experimental-hermetic-seal-at-link -lto=llvm-full 2>&1 | %FileCheck %s
// CHECK: swift
// CHECK: -enable-llvm-vfe
// CHECK: -enable-llvm-wme
// CHECK: -conditional-runtime-records
// CHECK: -internalize-at-link
// CHECK: -lto=llvm-full
// RUN: not %swiftc_driver -driver-print-jobs -target x86_64-apple-macosx10.9 %s \
// RUN: -experimental-hermetic-seal-at-link -enable-library-evolution 2>&1 | %FileCheck %s --check-prefix CHECK-LE
// CHECK-LE: error: Cannot use -experimental-hermetic-seal-at-link with -enable-library-evolution
// RUN: not %swiftc_driver -driver-print-jobs -target x86_64-apple-macosx10.9 %s \
// RUN: -experimental-hermetic-seal-at-link 2>&1 | %FileCheck %s --check-prefix CHECK-NOLTO
// CHECK-NOLTO: error: -experimental-hermetic-seal-at-link requires -lto=llvm-full or -lto=llvm-thin

View File

@@ -0,0 +1,72 @@
// End-to-end test of -experimental-hermetic-seal-at-link flag.
// RUN: %empty-directory(%t)
// (1) Build library swiftmodule
// RUN: %target-build-swift %s -DLIBRARY -module-name Library -experimental-hermetic-seal-at-link -lto=llvm-full %lto_flags \
// RUN: -Xfrontend -disable-reflection-metadata -Xfrontend -disable-reflection-names -Xfrontend -disable-objc-interop \
// RUN: -emit-library -static -o %t/libLibrary.a \
// RUN: -emit-module -emit-module-path %t/Library.swiftmodule
// (2) Check that libLibrary.a does actually provide all its public interfaces
// RUN: %llvm-nm %t/libLibrary.a | %FileCheck %s --check-prefix CHECK-NM-LIB
// (3) Build client
// RUN: %target-build-swift %s -DCLIENT -parse-as-library -module-name Main -experimental-hermetic-seal-at-link -lto=llvm-full %lto_flags \
// RUN: -Xfrontend -disable-reflection-metadata -Xfrontend -disable-reflection-names -Xfrontend -disable-objc-interop \
// RUN: -I%t -L%t -lLibrary -o %t/main
// (4) Check that unused symbols are not present in final executable
// RUN: %llvm-nm %t/main | %FileCheck %s --check-prefix CHECK-NM-EXEC
// (5) Execute
// RUN: %target-run %t/main | %FileCheck %s
// REQUIRES: executable_test
// Test disabled until LLVM GlobalDCE supports conditional references.
// REQUIRES: rdar81868900
#if LIBRARY
// The only symbol that's actually used by client code
public func used_func() {
print("used_func")
}
public class MyUnusedClass {}
public class MyUnusedSubClass: MyUnusedClass {}
public protocol MyUnusedProtocol {}
public struct MyUnusedStruct {}
public struct MyUnusedStruct2: MyUnusedProtocol {}
public enum MyUnusedEnum {}
public func MyUnusedFunc() {}
// (2) In libLibrary.a, all exported symbols are present...
// CHECK-NM-LIB-DAG: MyUnusedClass
// CHECK-NM-LIB-DAG: MyUnusedSubClass
// CHECK-NM-LIB-DAG: MyUnusedProtocol
// CHECK-NM-LIB-DAG: MyUnusedStruct
// CHECK-NM-LIB-DAG: MyUnusedStruct2
// CHECK-NM-LIB-DAG: MyUnusedEnum
// CHECK-NM-LIB-DAG: MyUnusedFunc
// (4) ... but after linking the main executable, they are removed.
// CHECK-NM-EXEC-NOT: MyUnused
#endif // LIBRARY
#if CLIENT
import Library
@_cdecl("main")
func main() -> Int32 {
used_func()
print("Done")
// CHECK: used_func
// CHECK-NEXT: Done
return 0
}
#endif // CLIENT