Skip to content

IRGen: Add code to support building fragile resilient protocol witnesses #72218

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions include/swift/AST/IRGenOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -467,6 +467,8 @@ class IRGenOptions {
/// Use relative (and constant) protocol witness tables.
unsigned UseRelativeProtocolWitnessTables : 1;

unsigned UseFragileResilientProtocolWitnesses : 1;

/// The number of threads for multi-threaded code generation.
unsigned NumThreads = 0;

Expand Down Expand Up @@ -555,6 +557,7 @@ class IRGenOptions {
EmitGenericRODatas(false), NoPreallocatedInstantiationCaches(false),
DisableReadonlyStaticObjects(false), CollocatedMetadataFunctions(false),
ColocateTypeDescriptors(true), UseRelativeProtocolWitnessTables(false),
UseFragileResilientProtocolWitnesses(false),
CmdArgs(), SanitizeCoverage(llvm::SanitizerCoverageOptions()),
TypeInfoFilter(TypeInfoDumpFilter::All),
PlatformCCallingConvention(llvm::CallingConv::C), UseCASBackend(false),
Expand Down
6 changes: 5 additions & 1 deletion include/swift/IRGen/TBDGen.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ struct TBDGenOptions {
/// Whether LLVM IR Witness Method Elimination is enabled.
bool WitnessMethodElimination = false;

/// Whether resilient protocols should be emitted fragile.
bool FragileResilientProtocols = false;

/// The install_name to use in the TBD file.
std::string InstallName;

Expand Down Expand Up @@ -78,6 +81,7 @@ struct TBDGenOptions {
lhs.PublicOrPackageSymbolsOnly == rhs.PublicOrPackageSymbolsOnly &&
lhs.VirtualFunctionElimination == rhs.VirtualFunctionElimination &&
lhs.WitnessMethodElimination == rhs.WitnessMethodElimination &&
lhs.FragileResilientProtocols == rhs.FragileResilientProtocols &&
lhs.InstallName == rhs.InstallName &&
lhs.ModuleLinkName == rhs.ModuleLinkName &&
lhs.CurrentVersion == rhs.CurrentVersion &&
Expand All @@ -95,7 +99,7 @@ struct TBDGenOptions {
return hash_combine(
opts.HasMultipleIGMs, opts.IsInstallAPI, opts.LinkerDirectivesOnly,
opts.PublicOrPackageSymbolsOnly, opts.VirtualFunctionElimination,
opts.WitnessMethodElimination,
opts.WitnessMethodElimination, opts.FragileResilientProtocols,
opts.InstallName, opts.ModuleLinkName,
opts.CurrentVersion, opts.CompatibilityVersion,
opts.ModuleInstallNameMapPath,
Expand Down
6 changes: 6 additions & 0 deletions include/swift/Option/FrontendOptions.td
Original file line number Diff line number Diff line change
Expand Up @@ -1260,6 +1260,12 @@ def enable_relative_protocol_witness_tables :
def disable_relative_protocol_witness_tables :
Flag<["-"], "disable-relative-protocol-witness-tables">,
HelpText<"Disable relative protocol witness tables">;
def enable_fragile_resilient_protocol_witnesses :
Flag<["-"], "enable-fragile-relative-protocol-tables">,
HelpText<"Enable relative protocol witness tables">;
def disable_fragile_resilient_protocol_witnesses :
Flag<["-"], "disable-fragile-relative-protocol-tables">,
HelpText<"Disable relative protocol witness tables">;

def enable_new_llvm_pass_manager :
Flag<["-"], "enable-new-llvm-pass-manager">,
Expand Down
3 changes: 3 additions & 0 deletions include/swift/SIL/SILSymbolVisitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ struct SILSymbolVisitorOptions {

/// Whether LLVM IR Witness Method Elimination is enabled.
bool WitnessMethodElimination = false;

/// Whether resilient protocols should be emitted fragile.
bool FragileResilientProtocols = false;
};

/// Context for `SILSymbolVisitor` symbol enumeration.
Expand Down
6 changes: 6 additions & 0 deletions lib/Frontend/CompilerInvocation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2521,6 +2521,8 @@ static bool ParseTBDGenArgs(TBDGenOptions &Opts, ArgList &Args,

Opts.VirtualFunctionElimination = Args.hasArg(OPT_enable_llvm_vfe);
Opts.WitnessMethodElimination = Args.hasArg(OPT_enable_llvm_wme);
Opts.FragileResilientProtocols =
Args.hasArg(OPT_enable_fragile_resilient_protocol_witnesses);

if (const Arg *A = Args.getLastArg(OPT_tbd_compatibility_version)) {
Opts.CompatibilityVersion = A->getValue();
Expand Down Expand Up @@ -3042,6 +3044,10 @@ static bool ParseIRGenArgs(IRGenOptions &Opts, ArgList &Args,
Args.hasFlag(OPT_enable_relative_protocol_witness_tables,
OPT_disable_relative_protocol_witness_tables,
Opts.UseRelativeProtocolWitnessTables);
Opts.UseFragileResilientProtocolWitnesses =
Args.hasFlag(OPT_enable_fragile_resilient_protocol_witnesses,
OPT_disable_fragile_resilient_protocol_witnesses,
Opts.UseFragileResilientProtocolWitnesses);
Opts.EnableLargeLoadableTypesReg2Mem =
Args.hasFlag(OPT_enable_large_loadable_types_reg2mem,
OPT_disable_large_loadable_types_reg2mem,
Expand Down
5 changes: 5 additions & 0 deletions lib/IRGen/GenDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6015,6 +6015,11 @@ bool IRGenModule::isResilient(NominalTypeDecl *D,
ClassDecl *asViewedFromRootClass) {
assert(!asViewedFromRootClass || isa<ClassDecl>(D));

// Ignore resilient protocols if requested.
if (isa<ProtocolDecl>(D) && IRGen.Opts.UseFragileResilientProtocolWitnesses) {
return false;
}

if (D->getModuleContext()->getBypassResilience())
return false;
if (expansion == ResilienceExpansion::Maximal &&
Expand Down
17 changes: 16 additions & 1 deletion lib/IRGen/GenProto.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -953,7 +953,10 @@ bool IRGenModule::isResilientConformance(
const NormalProtocolConformance *conformance) {
// If the protocol is not resilient, the conformance is not resilient
// either.
if (!conformance->getProtocol()->isResilient())
bool shouldTreatProtocolNonResilient =
IRGen.Opts.UseFragileResilientProtocolWitnesses;
if (!conformance->getProtocol()->isResilient() ||
shouldTreatProtocolNonResilient)
return false;

auto *conformanceModule = conformance->getDeclContext()->getParentModule();
Expand Down Expand Up @@ -2156,6 +2159,7 @@ namespace {
void addResilientWitnesses() {
if (Description.resilientWitnesses.empty())
return;
assert(!IGM.IRGen.Opts.UseFragileResilientProtocolWitnesses);

Flags = Flags.withHasResilientWitnesses(true);

Expand Down Expand Up @@ -2528,6 +2532,17 @@ void IRGenModule::emitSILWitnessTable(SILWitnessTable *wt) {
bool isResilient = isResilientConformance(conf);
bool useRelativeProtocolWitnessTable =
IRGen.Opts.UseRelativeProtocolWitnessTables;
if (useRelativeProtocolWitnessTable &&
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@slavapestov I am trying to test for variadic generics with conditional conformances here. Probably, doing it all wrong?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, for one a dump() isn't going to fly in an no-asserts build ...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks fine :)

!conf->getConditionalRequirements().empty()) {
auto sig = conf->getGenericSignature();
sig->forEachParam([&](GenericTypeParamType *param, bool canonical) {
if (param->isParameterPack()) {
#ifndef NDEBUG
wt->dump();
#endif
llvm::report_fatal_error("use of relative protcol witness tables not supported");
}});
}
if (!isResilient) {
// Build the witness table.
ConstantInitBuilder builder(*this);
Expand Down
1 change: 1 addition & 0 deletions lib/IRGen/TBDGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -511,6 +511,7 @@ void TBDGenVisitor::visit(const TBDGenDescriptor &desc) {
opts.PublicOrPackageSymbolsOnly = Opts.PublicOrPackageSymbolsOnly;
opts.WitnessMethodElimination = Opts.WitnessMethodElimination;
opts.VirtualFunctionElimination = Opts.VirtualFunctionElimination;
opts.FragileResilientProtocols = Opts.FragileResilientProtocols;

auto silVisitorCtx = SILSymbolVisitorContext(SwiftModule, opts);
auto visitorCtx = IRSymbolVisitorContext{UniversalLinkInfo, silVisitorCtx};
Expand Down
3 changes: 2 additions & 1 deletion lib/SIL/IR/SILSymbolVisitor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,8 @@ class SILSymbolVisitorImpl : public ASTVisitor<SILSymbolVisitorImpl> {
// We cannot emit the witness table symbol if the protocol is imported
// from another module and it's resilient, because initialization of that
// protocol is necessary in this case
if (!rootConformance->getProtocol()->isResilient(
if (Ctx.getOpts().FragileResilientProtocols ||
!rootConformance->getProtocol()->isResilient(
IDC->getAsGenericContext()->getParentModule(),
ResilienceExpansion::Maximal))
Visitor.addProtocolWitnessTable(rootConformance);
Expand Down
4 changes: 4 additions & 0 deletions stdlib/cmake/modules/AddSwiftStdlib.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -453,6 +453,10 @@ function(_add_target_variant_c_compile_flags)
list(APPEND result "-DSWIFT_STDLIB_USE_RELATIVE_PROTOCOL_WITNESS_TABLES")
endif()

if(SWIFT_STDLIB_USE_FRAGILE_RESILIENT_PROTOCOL_WITNESS_TABLES)
list(APPEND result "-DSWIFT_STDLIB_USE_FRAGILE_RESILIENT_PROTOCOL_WITNESS_TABLES")
endif()

if(SWIFT_STDLIB_OVERRIDABLE_RETAIN_RELEASE)
list(APPEND result "-DSWIFT_STDLIB_OVERRIDABLE_RETAIN_RELEASE")
endif()
Expand Down
4 changes: 4 additions & 0 deletions stdlib/cmake/modules/StdlibOptions.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,10 @@ option(SWIFT_STDLIB_USE_RELATIVE_PROTOCOL_WITNESS_TABLES
"Use relative protocol witness tables"
FALSE)

option(SWIFT_STDLIB_USE_FRAGILE_RESILIENT_PROTOCOL_WITNESS_TABLES
"Use fragile protocol witness tables for resilient protocols"
FALSE)

if("${SWIFT_HOST_VARIANT_SDK}" IN_LIST SWIFT_DARWIN_PLATFORMS)
set(SWIFT_STDLIB_INSTALL_PARENT_MODULE_FOR_SHIMS_default TRUE)
else()
Expand Down
5 changes: 5 additions & 0 deletions stdlib/cmake/modules/SwiftSource.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -626,6 +626,11 @@ function(_compile_swift_files
list(APPEND swift_flags "-Xfrontend" "-swift-async-frame-pointer=never")
endif()

if (SWIFT_STDLIB_USE_FRAGILE_RESILIENT_PROTOCOL_WITNESS_TABLES)
list(APPEND swift_flags "-Xfrontend" "-enable-fragile-relative-protocol-tables")
list(APPEND swift_flags "-enable-library-evolution")
endif()

if(SWIFT_STDLIB_DISABLE_INSTANTIATION_CACHES)
list(APPEND swift_flags "-Xfrontend" "-disable-preallocated-instantiation-caches")
endif()
Expand Down
15 changes: 15 additions & 0 deletions test/IRGen/Inputs/relative_protocol_witness_tables2.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
public protocol ResilientProto {
associatedtype T
func impl()
}


public struct ResilientStruct<T> : ResilientProto {
var x : T?
public init(_ t: T) {
x = t
}
public func impl() {
print(x)
}
}
41 changes: 41 additions & 0 deletions test/IRGen/relative_protocol_witness_table.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,21 @@
// RUN: %target-swift-frontend -enable-relative-protocol-witness-tables -module-name A -primary-file %s -emit-ir | %FileCheck %s --check-prefix=CHECK-%target-cpu --check-prefix=CHECK

// Test with resilience enabled.
// In this mode we still assume protocols to be "fragile"/non changeable.
// RUN: %target-swift-frontend -enable-resilience -enable-fragile-relative-protocol-tables -enable-relative-protocol-witness-tables -module-name A -primary-file %s -I %t -emit-ir | %FileCheck %s --check-prefix=CHECK-%target-cpu --check-prefix=CHECK
// RUN: %empty-directory(%t)
// RUN: %target-swift-frontend -emit-module -enable-fragile-relative-protocol-tables -enable-library-evolution -enable-relative-protocol-witness-tables -emit-module-path=%t/resilient.swiftmodule -module-name=resilient %S/Inputs/relative_protocol_witness_tables2.swift
// Inputs/relative_protocol_witness_tables2.swift
// RUN: %target-swift-frontend -enable-fragile-relative-protocol-tables -enable-relative-protocol-witness-tables -module-name A -primary-file %s -I %t -emit-ir -DWITH_RESILIENCE | %FileCheck %s --check-prefix=CHECK-%target-cpu --check-prefix=CHECK
// RUN: %target-swift-frontend -enable-fragile-relative-protocol-tables -enable-relative-protocol-witness-tables -module-name A -primary-file %s -I %t -emit-ir -DWITH_RESILIENCE | %FileCheck %s --check-prefix=EVO
// RUN: not --crash %target-swift-frontend -enable-fragile-relative-protocol-tables -enable-relative-protocol-witness-tables -module-name A -primary-file %s -I %t -emit-ir -DWITH_RESILIENCE -DEXPECT_CRASH 2>&1 | %FileCheck %s --check-prefix=CRASH

// REQUIRES: CPU=x86_64 || CPU=arm64 || CPU=arm64e

#if WITH_RESILIENCE
import resilient
#endif

func testVWT<T>(_ t: T) {
var local = t
}
Expand Down Expand Up @@ -323,3 +337,30 @@ func instantiate_conditional_conformance_2nd<T>(_ t : T) where T: Sub, T.S == T
// CHECK: [[T26:%.*]] = call ptr @swift_getWitnessTableRelative({{.*}}@"$s1A1XVyxGAA4BaseA2aERzlMc{{.*}}, ptr {{.*}}, ptr [[T24]])
// CHECK: [[T28:%.*]] = getelementptr inbounds ptr, ptr [[C0]], i32 1
// CHECK: store ptr [[T26]], ptr [[T28]]

#if WITH_RESILIENCE
public func requireFormallyResilientWitness<T: ResilientProto> (_ t: T) {
t.impl()
}

// EVO: define{{.*}} swiftcc void @"$s1A27useFormallyResilientWitnessyyF"()
// EVO: call swiftcc void @"$s1A31requireFormallyResilientWitnessyyx9resilient0C5ProtoRzlF"(ptr noalias %4, ptr %1, ptr @"$s9resilient15ResilientStructVyxGAA0B5ProtoAAWP")
public func useFormallyResilientWitness() {
requireFormallyResilientWitness(ResilientStruct(1))
}
#endif

#if EXPECT_CRASH
protocol P {
func p()
}

@available(SwiftStdlib 5.9, *)
struct G<each T> {}

@available(SwiftStdlib 5.9, *)
extension G: P where repeat each T: P {
func p() {}
}
// CRASH: not supported
#endif
5 changes: 4 additions & 1 deletion test/lit.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -1174,7 +1174,7 @@ if run_vendor == 'apple':

# TODO: consider making the addition of these flags dependent on the CMake
# setting for hermetic seal at link
if not config.swift_freestanding_is_darwin:
if not config.swift_freestanding_is_darwin and not config.swift_stdlib_use_use_fragile_resilient_protocol_witness_tables:
swift_execution_tests_extra_flags += ' -experimental-hermetic-seal-at-link -lto=llvm-full'

if not config.swift_freestanding_is_darwin:
Expand All @@ -1186,6 +1186,9 @@ if run_vendor == 'apple':
if config.swift_stdlib_use_relative_protocol_witness_tables:
swift_execution_tests_extra_flags += ' -Xfrontend -enable-relative-protocol-witness-tables -Xfrontend -swift-async-frame-pointer=never'

if config.swift_stdlib_use_use_fragile_resilient_protocol_witness_tables:
swift_execution_tests_extra_flags += ' -Xfrontend -enable-fragile-relative-protocol-tables'

# Build a resource dir for freestanding tests.
new_resource_dir = os.path.join(config.test_exec_root, "resource_dir")
if not os.path.exists(new_resource_dir): os.mkdir(new_resource_dir)
Expand Down
1 change: 1 addition & 0 deletions test/lit.site.cfg.in
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ if "@SWIFT_ENABLE_SYNCHRONIZATION@" == "TRUE":

config.swift_freestanding_is_darwin = "@SWIFT_FREESTANDING_IS_DARWIN@" == "TRUE"
config.swift_stdlib_use_relative_protocol_witness_tables = "@SWIFT_STDLIB_USE_RELATIVE_PROTOCOL_WITNESS_TABLES@" == "TRUE"
config.swift_stdlib_use_use_fragile_resilient_protocol_witness_tables = "@SWIFT_STDLIB_USE_FRAGILE_RESILIENT_PROTOCOL_WITNESS_TABLES@" == "TRUE"
config.swift_enable_dispatch = "@SWIFT_ENABLE_DISPATCH@" == "TRUE"
config.swift_stdlib_enable_objc_interop = "@SWIFT_STDLIB_ENABLE_OBJC_INTEROP@" == "TRUE"
# Configured in DarwinSDKs.cmake
Expand Down
36 changes: 36 additions & 0 deletions utils/build-presets.ini
Original file line number Diff line number Diff line change
Expand Up @@ -2741,6 +2741,42 @@ swift-stdlib-use-relative-protocol-witness-tables=1
test
validation-test

[preset: stdlib_S_standalone_minimal_macho_arm64_relative_resilient_protocol_witness_table,build,test]
mixin-preset=
stdlib_base_standalone
mixin_stdlib_minimal

build-subdir=stdlib_S_standalone
min-size-release
assertions
swift-stdlib-enable-assertions=false

verbose-build

swift-primary-variant-sdk=FREESTANDING
swift-primary-variant-arch=arm64
swift-freestanding-flavor=apple
swift-freestanding-sdk=macosx
stdlib-deployment-targets=freestanding-arm64

# For now, until clang/swiftc works correctly with "none-macho" as the OS part of target triple.
swift-freestanding-triple-name=macosx11.0
swift-freestanding-module-name=macos
swift-freestanding-archs=arm64

# For lit tests, we are producing dynamic executables with statically linked stdlib into the executable.
swift-runtime-static-image-inspection=0

swift-stdlib-experimental-hermetic-seal-at-link=0
swift-stdlib-lto=
swift-enable-runtime-function-counters=0
swift-stdlib-use-relative-protocol-witness-tables=1
swift-stdlib-use-fragile-resilient-protocol-witness-tables=1

test
validation-test
only-non-executable-test

[preset: stdlib_S_standalone_darwin_x86_64,build]
mixin-preset=
stdlib_S_standalone,build
Expand Down
8 changes: 8 additions & 0 deletions utils/build-script-impl
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,7 @@ KNOWN_SETTINGS=(
swift-stdlib-tracing "" "whether to enable tracing signposts for the stdlib; default is 1 on Darwin platforms, 0 otherwise"
swift-stdlib-concurrency-tracing "" "whether to enable tracing signposts for concurrency; default is 1 on Darwin platforms, 0 otherwise"
swift-stdlib-use-relative-protocol-witness-tables "0" "whether to use relative protocol witness table"
swift-stdlib-use-fragile-resilient-protocol-witness-tables "0" "whether to use fragile resilient protocol witness table"
swift-enable-runtime-function-counters "" "whether to enable runtime function counters"
swift-stdlib-os-versioning "1" "whether to build stdlib with availability based on OS versions (Darwin only)"
swift-stdlib-has-commandline "1" "whether to build stdlib with the CommandLine enum and support for argv/argc"
Expand Down Expand Up @@ -2003,6 +2004,13 @@ for host in "${ALL_HOSTS[@]}"; do
)
fi

if [[ "${SWIFT_STDLIB_USE_FRAGILE_RESILIENT_PROTOCOL_WITNESS_TABLES}" ]] ; then
cmake_options=(
"${cmake_options[@]}"
-DSWIFT_STDLIB_USE_FRAGILE_RESILIENT_PROTOCOL_WITNESS_TABLES:BOOL=$(true_false "${SWIFT_STDLIB_USE_FRAGILE_RESILIENT_PROTOCOL_WITNESS_TABLES}")
)
fi

if [[ "${SWIFT_RUNTIME_FIXED_BACKTRACER_PATH}" ]] ; then
cmake_options=(
"${cmake_options[@]}"
Expand Down
1 change: 1 addition & 0 deletions validation-test/lit.site.cfg.in
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ if "@SWIFT_STDLIB_ENABLE_DEBUG_PRECONDITIONS_IN_RELEASE@" == "TRUE":

config.swift_freestanding_is_darwin = "@SWIFT_FREESTANDING_IS_DARWIN@" == "TRUE"
config.swift_stdlib_use_relative_protocol_witness_tables = "@SWIFT_STDLIB_USE_RELATIVE_PROTOCOL_WITNESS_TABLES@" == "TRUE"
config.swift_stdlib_use_use_fragile_resilient_protocol_witness_tables = "@SWIFT_STDLIB_USE_FRAGILE_RESILIENT_PROTOCOL_WITNESS_TABLES@" == "TRUE"
config.swift_enable_dispatch = "@SWIFT_ENABLE_DISPATCH@" == "TRUE"
config.swift_stdlib_enable_objc_interop = "@SWIFT_STDLIB_ENABLE_OBJC_INTEROP@" == "TRUE"
# Configured in DarwinSDKs.cmake
Expand Down