Skip to content

IRGen: support static linking on Windows #37211

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 2 commits into from
May 5, 2021
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
5 changes: 4 additions & 1 deletion include/swift/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -588,7 +588,10 @@ class alignas(1 << DeclAlignInBits) Decl {
HasAnyUnavailableValues : 1
);

SWIFT_INLINE_BITFIELD(ModuleDecl, TypeDecl, 1+1+1+1+1+1+1+1+1+1,
SWIFT_INLINE_BITFIELD(ModuleDecl, TypeDecl, 1+1+1+1+1+1+1+1+1+1+1,
/// If the module is compiled as static library.
StaticLibrary : 1,

/// If the module was or is being compiled with `-enable-testing`.
TestingEnabled : 1,

Expand Down
8 changes: 8 additions & 0 deletions include/swift/AST/Module.h
Original file line number Diff line number Diff line change
Expand Up @@ -424,6 +424,14 @@ class ModuleDecl : public DeclContext, public TypeDecl {
DebugClient = R;
}

/// Returns true if this module is compiled as static library.
bool isStaticLibrary() const {
return Bits.ModuleDecl.StaticLibrary;
}
void setStaticLibrary(bool isStatic = true) {
Bits.ModuleDecl.StaticLibrary = isStatic;
}

/// Returns true if this module was or is being compiled for testing.
bool isTestingEnabled() const {
return Bits.ModuleDecl.TestingEnabled;
Expand Down
3 changes: 3 additions & 0 deletions include/swift/Frontend/FrontendOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,9 @@ class FrontendOptions {
/// skip nodes entirely, depending on the errors involved.
bool AllowModuleWithCompilerErrors = false;

/// True if the "-static" option is set.
bool Static = false;

/// The different modes for validating TBD against the LLVM IR.
enum class TBDValidationMode {
Default, ///< Do the default validation for the current platform.
Expand Down
1 change: 1 addition & 0 deletions include/swift/Serialization/SerializationOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ namespace swift {
bool SerializeOptionsForDebugging = false;
bool IsSIB = false;
bool DisableCrossModuleIncrementalInfo = false;
bool StaticLibrary = false;
};

} // end namespace swift
Expand Down
5 changes: 5 additions & 0 deletions include/swift/Serialization/Validation.h
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ class ExtendedValidationInfo {
struct {
unsigned ArePrivateImportsEnabled : 1;
unsigned IsSIB : 1;
unsigned IsStaticLibrary: 1;
unsigned IsTestable : 1;
unsigned ResilienceStrategy : 2;
unsigned IsImplicitDynamicEnabled : 1;
Expand Down Expand Up @@ -131,6 +132,10 @@ class ExtendedValidationInfo {
void setImplicitDynamicEnabled(bool val) {
Bits.IsImplicitDynamicEnabled = val;
}
bool isStaticLibrary() const { return Bits.IsStaticLibrary; }
void setIsStaticLibrary(bool val) {
Bits.IsStaticLibrary = val;
}
bool isTestable() const { return Bits.IsTestable; }
void setIsTestable(bool val) {
Bits.IsTestable = val;
Expand Down
1 change: 1 addition & 0 deletions lib/AST/Module.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -475,6 +475,7 @@ ModuleDecl::ModuleDecl(Identifier name, ASTContext &ctx,

setAccess(AccessLevel::Public);

Bits.ModuleDecl.StaticLibrary = 0;
Bits.ModuleDecl.TestingEnabled = 0;
Bits.ModuleDecl.FailedToLoad = 0;
Bits.ModuleDecl.RawResilienceStrategy = 0;
Expand Down
2 changes: 2 additions & 0 deletions lib/Frontend/ArgsToFrontendOptionsConverter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,8 @@ bool ArgsToFrontendOptionsConverter::convert(

Opts.SkipInheritedDocs = Args.hasArg(OPT_skip_inherited_docs);

Opts.Static = Args.hasArg(OPT_static);

return false;
}

Expand Down
2 changes: 2 additions & 0 deletions lib/Frontend/Frontend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,8 @@ SerializationOptions CompilerInvocation::computeSerializationOptions(
serializationOpts.DisableCrossModuleIncrementalInfo =
opts.DisableCrossModuleIncrementalBuild;

serializationOpts.StaticLibrary = opts.Static;

return serializationOpts;
}

Expand Down
6 changes: 4 additions & 2 deletions lib/IRGen/GenDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2030,7 +2030,7 @@ void irgen::updateLinkageForDefinition(IRGenModule &IGM,
bool isKnownLocal = entity.isAlwaysSharedLinkage();
if (const auto *DC = entity.getDeclContextForEmission())
if (const auto *MD = DC->getParentModule())
isKnownLocal = IGM.getSwiftModule() == MD;
isKnownLocal = IGM.getSwiftModule() == MD || MD->isStaticLibrary();

auto IRL =
getIRLinkage(linkInfo, entity.getLinkage(ForDefinition),
Expand Down Expand Up @@ -2059,7 +2059,7 @@ LinkInfo LinkInfo::get(const UniversalLinkageInfo &linkInfo,
bool isKnownLocal = entity.isAlwaysSharedLinkage();
if (const auto *DC = entity.getDeclContextForEmission())
if (const auto *MD = DC->getParentModule())
isKnownLocal = MD == swiftModule;
isKnownLocal = MD == swiftModule || MD->isStaticLibrary();

entity.mangle(result.Name);
bool weakImported = entity.isWeakImported(swiftModule);
Expand All @@ -2074,6 +2074,8 @@ LinkInfo LinkInfo::get(const UniversalLinkageInfo &linkInfo, StringRef name,
bool isWeakImported) {
LinkInfo result;

// TODO(compnerd) handle this properly

result.Name += name;
result.IRL = getIRLinkage(linkInfo, linkage, isDefinition, isWeakImported);
result.ForDefinition = isDefinition;
Expand Down
12 changes: 9 additions & 3 deletions lib/IRGen/GenProto.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1117,9 +1117,15 @@ class DirectConformanceInfo : public ConformanceInfo {

llvm::Constant *tryGetConstantTable(IRGenModule &IGM,
CanType conformingType) const override {
if (IGM.getOptions().LazyInitializeProtocolConformances &&
RootConformance->getDeclContext()->getParentModule() != IGM.getSwiftModule())
return nullptr;
if (IGM.getOptions().LazyInitializeProtocolConformances) {
const auto *MD = RootConformance->getDeclContext()->getParentModule();
// If the protocol conformance is defined in the current module or the
// module will be statically linked, then we can statically initialize the
// conformance as we know that the protocol conformance is guaranteed to
// be present.
if (!(MD == IGM.getSwiftModule() || MD->isStaticLibrary()))
return nullptr;
}
return IGM.getAddrOfWitnessTable(RootConformance);
}
};
Expand Down
5 changes: 5 additions & 0 deletions lib/Serialization/ModuleFile.h
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,11 @@ class ModuleFile
return Core->Bits.IsTestable;
}

/// Whether this module is compiled as static library.
bool isStaticLibrary() const {
return Core->Bits.IsStaticLibrary;
}

/// Whether the module is resilient. ('-enable-library-evolution')
ResilienceStrategy getResilienceStrategy() const {
return ResilienceStrategy(Core->Bits.ResilienceStrategy);
Expand Down
4 changes: 4 additions & 0 deletions lib/Serialization/ModuleFileSharedCore.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,9 @@ static bool readOptionsBlock(llvm::BitstreamCursor &cursor,
options_block::IsSIBLayout::readRecord(scratch, IsSIB);
extendedInfo.setIsSIB(IsSIB);
break;
case options_block::IS_STATIC_LIBRARY:
extendedInfo.setIsStaticLibrary(true);
break;
case options_block::IS_TESTABLE:
extendedInfo.setIsTestable(true);
break;
Expand Down Expand Up @@ -1180,6 +1183,7 @@ ModuleFileSharedCore::ModuleFileSharedCore(
UserModuleVersion = info.userModuleVersion;
Bits.ArePrivateImportsEnabled = extInfo.arePrivateImportsEnabled();
Bits.IsSIB = extInfo.isSIB();
Bits.IsStaticLibrary = extInfo.isStaticLibrary();
Bits.IsTestable = extInfo.isTestable();
Bits.ResilienceStrategy = unsigned(extInfo.getResilienceStrategy());
Bits.IsImplicitDynamicEnabled = extInfo.isImplicitDynamicEnabled();
Expand Down
3 changes: 3 additions & 0 deletions lib/Serialization/ModuleFileSharedCore.h
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,9 @@ class ModuleFileSharedCore {
/// Whether this module file is actually a .sib file.
unsigned IsSIB: 1;

/// Whether this module is compiled as static library.
unsigned IsStaticLibrary: 1;

/// Whether this module file is compiled with '-enable-testing'.
unsigned IsTestable : 1;

Expand Down
7 changes: 6 additions & 1 deletion lib/Serialization/ModuleFormat.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0;
/// describe what change you made. The content of this comment isn't important;
/// it just ensures a conflict if two people change the module format.
/// Don't worry about adhering to the 80-column limit for this line.
const uint16_t SWIFTMODULE_VERSION_MINOR = 612; // add user-defined module version
const uint16_t SWIFTMODULE_VERSION_MINOR = 613; // isStaticLibrary option

/// A standard hash seed used for all string hashes in a serialized module.
///
Expand Down Expand Up @@ -788,6 +788,7 @@ namespace options_block {
SDK_PATH = 1,
XCC,
IS_SIB,
IS_STATIC_LIBRARY,
IS_TESTABLE,
RESILIENCE_STRATEGY,
ARE_PRIVATE_IMPORTS_ENABLED,
Expand All @@ -811,6 +812,10 @@ namespace options_block {
BCFixed<1> // Is this an intermediate file?
>;

using IsStaticLibraryLayout = BCRecordLayout<
IS_STATIC_LIBRARY
>;

using IsTestableLayout = BCRecordLayout<
IS_TESTABLE
>;
Expand Down
5 changes: 5 additions & 0 deletions lib/Serialization/Serialization.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -981,6 +981,11 @@ void Serializer::writeHeader(const SerializationOptions &options) {
options_block::IsSIBLayout IsSIB(Out);
IsSIB.emit(ScratchRecord, options.IsSIB);

if (options.StaticLibrary) {
options_block::IsStaticLibraryLayout IsStaticLibrary(Out);
IsStaticLibrary.emit(ScratchRecord);
}

if (M->isTestingEnabled()) {
options_block::IsTestableLayout IsTestable(Out);
IsTestable.emit(ScratchRecord);
Expand Down
1 change: 1 addition & 0 deletions lib/Serialization/SerializedModuleLoader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -715,6 +715,7 @@ LoadedFile *SerializedModuleLoaderBase::loadAST(

// We've loaded the file. Now try to bring it into the AST.
fileUnit = new (Ctx) SerializedASTFile(M, *loadedModuleFile);
M.setStaticLibrary(loadedModuleFile->isStaticLibrary());
if (loadedModuleFile->isTestable())
M.setTestingEnabled();
if (loadedModuleFile->arePrivateImportsEnabled())
Expand Down
65 changes: 65 additions & 0 deletions test/IRGen/windows-linking.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// RUN: %empty-directory(%t)
// RUN: %target-swift-frontend -target x86_64-unknown-windows-msvc -parse-as-library -parse-stdlib -static -emit-module -emit-module-path %t/module.swiftmodule -module-name module -DMODULE %s
// RUN: %target-swift-frontend -target x86_64-unknown-windows-msvc -parse-as-library -parse-stdlib -S -emit-ir %s -module-name main -o - -I%t | %FileCheck %s -check-prefix CHECK-STATIC
// RUN: %target-swift-frontend -target x86_64-unknown-windows-msvc -parse-as-library -parse-stdlib -emit-module -emit-module-path %t/module.swiftmodule -module-name module -DMODULE %s
// RUN: %target-swift-frontend -target x86_64-unknown-windows-msvc -parse-as-library -parse-stdlib -S -emit-ir %s -module-name main -o - -I%t | %FileCheck %s -check-prefix CHECK-SHARED

#if MODULE

public struct S {}
public var value: S {
S()
}

public func f(_ s: S) {}

public protocol P {
}

public enum E: P {
}

#else

import module

protocol Q: P {
}

extension E: Q {
}

@main
struct Entry {
public static func main() {
f(value)
}
}

#endif


// Ensure that static linking does not mark the entries as being indirected
// through the IAT.

// CHECK-STATIC: @"$s6module1EO4main1QADWP" = hidden constant [2 x i8*] [
// CHECK-STATIC-SAME: i8* bitcast (%swift.protocol_conformance_descriptor* @"$s6module1EO4main1QADMc" to i8*),
// CHECK-STATIC-SAME: i8* bitcast (i8** @"$s6module1EOAA1PAAWP" to i8*)
// CHECK-STATIC-SAME: ]

// CHECK-STATIC: declare swiftcc void @"$s6module5valueAA1SVvg"()

// CHECK-STATIC: declare swiftcc void @"$s6module1fyyAA1SVF"()


// Ensure that shared linking does mark the functions as being indirected
// through the IAT.

// CHECK-SHARED: @"$s6module1EO4main1QADWP" = hidden constant [2 x i8*] [
// CHECK-SHARED-SAME: i8* bitcast (%swift.protocol_conformance_descriptor* @"$s6module1EO4main1QADMc" to i8*),
// CHECK-SHARED-SAME: i8* null
// CHECK-SHARED-SAME: ]

// CHECK-SHARED: declare dllimport swiftcc void @"$s6module5valueAA1SVvg"()

// CHECK-SHARED: declare dllimport swiftcc void @"$s6module1fyyAA1SVF"()