Skip to content

Commit 455af5c

Browse files
committed
SIL: Serialize availability with the SIL function, not just a weak imported flag
The weak imported flag is now only set if the attribute is unconditionally weak linked, which is the case when it or one of its parent contexts has a @_weakLinked attribute. To correctly handle weak linking based availability with serialized SIL functions, we need to serialize the actual version tuple when the SIL function was introduced. This is because the deployment target of the client app can be older than the deployment target that the original module was built with. Fixes <rdar://problem/52783668>.
1 parent c7d7354 commit 455af5c

21 files changed

+298
-120
lines changed

include/swift/AST/DiagnosticsParse.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -631,6 +631,8 @@ ERROR(expected_sil_function_type, none,
631631
"sil function expected to have SIL function type", ())
632632
ERROR(sil_dynamically_replaced_func_not_found,none,
633633
"dynamically replaced function not found %0", (Identifier))
634+
ERROR(sil_availability_expected_version,none,
635+
"expected version number in 'available' attribute", ())
634636

635637
// SIL Stage
636638
ERROR(expected_sil_stage_name, none,

include/swift/SIL/SILFunction.h

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#define SWIFT_SIL_SILFUNCTION_H
1919

2020
#include "swift/AST/ASTNode.h"
21+
#include "swift/AST/Availability.h"
2122
#include "swift/AST/ResilienceExpansion.h"
2223
#include "swift/Basic/ProfileCounter.h"
2324
#include "swift/SIL/SILBasicBlock.h"
@@ -173,6 +174,10 @@ class SILFunction
173174
/// Contains Function Entry Count
174175
ProfileCounter EntryCount;
175176

177+
/// The availability used to determine if declarations of this function
178+
/// should use weak linking.
179+
AvailabilityContext Availability;
180+
176181
/// This is the number of uses of this SILFunction inside the SIL.
177182
/// It does not include references from debug scopes.
178183
unsigned RefCount = 0;
@@ -211,8 +216,9 @@ class SILFunction
211216
/// would indicate.
212217
unsigned HasCReferences : 1;
213218

214-
/// Whether cross-module references to this function should use weak linking.
215-
unsigned IsWeakLinked : 1;
219+
/// Whether cross-module references to this function should always use
220+
/// weak linking.
221+
unsigned IsWeakImported : 1;
216222

217223
// Whether the implementation can be dynamically replaced.
218224
unsigned IsDynamicReplaceable : 1;
@@ -575,17 +581,27 @@ class SILFunction
575581
bool hasCReferences() const { return HasCReferences; }
576582
void setHasCReferences(bool value) { HasCReferences = value; }
577583

584+
/// Returns the availability context used to determine if the function's
585+
/// symbol should be weakly referenced across module boundaries.
586+
AvailabilityContext getAvailabilityForLinkage() const {
587+
return Availability;
588+
}
589+
590+
void setAvailabilityForLinkage(AvailabilityContext availability) {
591+
Availability = availability;
592+
}
593+
578594
/// Returns whether this function's symbol must always be weakly referenced
579595
/// across module boundaries.
580-
bool isWeakLinked() const { return IsWeakLinked; }
581-
/// Forces IRGen to treat references to this function as weak across module
582-
/// boundaries (i.e. if it has external linkage).
583-
void setWeakLinked(bool value = true) {
584-
assert(!IsWeakLinked && "already set");
585-
IsWeakLinked = value;
596+
bool isAlwaysWeakImported() const { return IsWeakImported; }
597+
598+
void setAlwaysWeakImported(bool value) {
599+
IsWeakImported = value;
586600
}
587601

588-
/// Returs whether this function implementation can be dynamically replaced.
602+
bool isWeakImported() const;
603+
604+
/// Returns whether this function implementation can be dynamically replaced.
589605
IsDynamicallyReplaceable_t isDynamicallyReplaceable() const {
590606
return IsDynamicallyReplaceable_t(IsDynamicReplaceable);
591607
}

include/swift/Serialization/ModuleFormat.h

Lines changed: 39 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0;
5252
/// describe what change you made. The content of this comment isn't important;
5353
/// it just ensures a conflict if two people change the module format.
5454
/// Don't worry about adhering to the 80-column limit for this line.
55-
const uint16_t SWIFTMODULE_VERSION_MINOR = 500; // distinguish implicit raw values for enum cases
55+
const uint16_t SWIFTMODULE_VERSION_MINOR = 501; // SIL function availability
5656

5757
using DeclIDField = BCFixed<31>;
5858

@@ -473,6 +473,44 @@ enum class ImportControl : uint8_t {
473473
};
474474
using ImportControlField = BCFixed<2>;
475475

476+
// Encodes a VersionTuple:
477+
//
478+
// Major
479+
// Minor
480+
// Subminor
481+
// HasMinor
482+
// HasSubminor
483+
#define BC_AVAIL_TUPLE\
484+
BCVBR<5>,\
485+
BCVBR<5>,\
486+
BCVBR<4>,\
487+
BCFixed<1>,\
488+
BCFixed<1>
489+
490+
#define LIST_VER_TUPLE_PIECES(X)\
491+
X##_Major, X##_Minor, X##_Subminor, X##_HasMinor, X##_HasSubminor
492+
#define DEF_VER_TUPLE_PIECES(X) unsigned LIST_VER_TUPLE_PIECES(X)
493+
#define DECODE_VER_TUPLE(X)\
494+
if (X##_HasMinor) {\
495+
if (X##_HasSubminor)\
496+
X = llvm::VersionTuple(X##_Major, X##_Minor, X##_Subminor);\
497+
else\
498+
X = llvm::VersionTuple(X##_Major, X##_Minor);\
499+
}\
500+
else X = llvm::VersionTuple(X##_Major);
501+
#define ENCODE_VER_TUPLE(X, X_Expr)\
502+
unsigned X##_Major = 0, X##_Minor = 0, X##_Subminor = 0,\
503+
X##_HasMinor = 0, X##_HasSubminor = 0;\
504+
const auto &X##_Val = X_Expr;\
505+
if (X##_Val.hasValue()) {\
506+
const auto &Y = X##_Val.getValue();\
507+
X##_Major = Y.getMajor();\
508+
X##_Minor = Y.getMinor().getValueOr(0);\
509+
X##_Subminor = Y.getSubminor().getValueOr(0);\
510+
X##_HasMinor = Y.getMinor().hasValue();\
511+
X##_HasSubminor = Y.getSubminor().hasValue();\
512+
}
513+
476514
/// The various types of blocks that can occur within a serialized Swift
477515
/// module.
478516
///
@@ -1596,20 +1634,6 @@ namespace decls_block {
15961634
BCFixed<2> // optimize value
15971635
>;
15981636

1599-
// Encodes a VersionTuple:
1600-
//
1601-
// Major
1602-
// Minor
1603-
// Subminor
1604-
// HasMinor
1605-
// HasSubminor
1606-
#define BC_AVAIL_TUPLE\
1607-
BCVBR<5>,\
1608-
BCVBR<5>,\
1609-
BCVBR<4>,\
1610-
BCFixed<1>,\
1611-
BCFixed<1>
1612-
16131637
using AvailableDeclAttrLayout = BCRecordLayout<
16141638
Available_DECL_ATTR,
16151639
BCFixed<1>, // implicit flag
@@ -1625,8 +1649,6 @@ namespace decls_block {
16251649
BCBlob // platform, followed by message
16261650
>;
16271651

1628-
#undef BC_AVAIL_TUPLE
1629-
16301652
using ObjCDeclAttrLayout = BCRecordLayout<
16311653
ObjC_DECL_ATTR,
16321654
BCFixed<1>, // implicit flag

lib/IRGen/Linking.cpp

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
#include "IRGenMangler.h"
1919
#include "IRGenModule.h"
2020
#include "swift/AST/ASTMangler.h"
21-
#include "swift/AST/Availability.h"
2221
#include "swift/AST/IRGenOptions.h"
2322
#include "swift/ClangImporter/ClangModule.h"
2423
#include "swift/SIL/SILGlobalVariable.h"
@@ -954,22 +953,13 @@ bool LinkEntity::isWeakImported(ModuleDecl *module) const {
954953
switch (getKind()) {
955954
case Kind::SILGlobalVariable:
956955
if (getSILGlobalVariable()->getDecl()) {
957-
return getSILGlobalVariable()->getDecl()
958-
->isWeakImported(module);
956+
return getSILGlobalVariable()->getDecl()->isWeakImported(module);
959957
}
960958
return false;
961959
case Kind::DynamicallyReplaceableFunctionKey:
962960
case Kind::DynamicallyReplaceableFunctionVariable:
963961
case Kind::SILFunction: {
964-
// For imported functions check the Clang declaration.
965-
if (auto clangOwner = getSILFunction()->getClangNodeOwner())
966-
return clangOwner->isWeakImported(module);
967-
968-
// For native functions check a flag on the SILFunction
969-
// itself.
970-
if (getSILFunction()->isWeakLinked())
971-
return getSILFunction()->isAvailableExternally();
972-
return false;
962+
return getSILFunction()->isWeakImported();
973963
}
974964

975965
case Kind::AssociatedConformanceDescriptor:

lib/ParseSIL/ParseSIL.cpp

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -981,7 +981,9 @@ static bool parseDeclSILOptional(bool *isTransparent,
981981
bool *isGlobalInit,
982982
Inline_t *inlineStrategy,
983983
OptimizationMode *optimizationMode,
984-
bool *isLet, bool *isWeakLinked,
984+
bool *isLet,
985+
bool *isWeakImported,
986+
AvailabilityContext *availability,
985987
bool *isWithoutActuallyEscapingThunk,
986988
SmallVectorImpl<std::string> *Semantics,
987989
SmallVectorImpl<ParsedSpecAttr> *SpecAttrs,
@@ -1021,14 +1023,27 @@ static bool parseDeclSILOptional(bool *isTransparent,
10211023
*isWithoutActuallyEscapingThunk = true;
10221024
else if (isGlobalInit && SP.P.Tok.getText() == "global_init")
10231025
*isGlobalInit = true;
1024-
else if (isWeakLinked && SP.P.Tok.getText() == "_weakLinked")
1026+
else if (isWeakImported && SP.P.Tok.getText() == "weak_imported") {
10251027
if (M.getASTContext().LangOpts.Target.isOSBinFormatCOFF())
10261028
SP.P.diagnose(SP.P.Tok, diag::attr_unsupported_on_target,
10271029
SP.P.Tok.getText(),
10281030
M.getASTContext().LangOpts.Target.str());
10291031
else
1030-
*isWeakLinked = true;
1031-
else if (inlineStrategy && SP.P.Tok.getText() == "noinline")
1032+
*isWeakImported = true;
1033+
} else if (availability && SP.P.Tok.getText() == "available") {
1034+
SP.P.consumeToken(tok::identifier);
1035+
1036+
SourceRange range;
1037+
llvm::VersionTuple version;
1038+
if (SP.P.parseVersionTuple(version, range,
1039+
diag::sil_availability_expected_version))
1040+
return true;
1041+
1042+
*availability = AvailabilityContext(VersionRange::allGTE(version));
1043+
1044+
SP.P.parseToken(tok::r_square, diag::expected_in_attribute_list);
1045+
continue;
1046+
} else if (inlineStrategy && SP.P.Tok.getText() == "noinline")
10321047
*inlineStrategy = NoInline;
10331048
else if (optimizationMode && SP.P.Tok.getText() == "Onone")
10341049
*optimizationMode = OptimizationMode::NoOptimization;
@@ -5352,7 +5367,8 @@ bool SILParserTUState::parseDeclSIL(Parser &P) {
53525367
IsDynamicallyReplaceable_t isDynamic = IsNotDynamic;
53535368
bool hasOwnershipSSA = false;
53545369
IsThunk_t isThunk = IsNotThunk;
5355-
bool isGlobalInit = false, isWeakLinked = false;
5370+
bool isGlobalInit = false, isWeakImported = false;
5371+
AvailabilityContext availability = AvailabilityContext::alwaysAvailable();
53565372
bool isWithoutActuallyEscapingThunk = false;
53575373
Inline_t inlineStrategy = InlineDefault;
53585374
OptimizationMode optimizationMode = OptimizationMode::NotSet;
@@ -5367,7 +5383,7 @@ bool SILParserTUState::parseDeclSIL(Parser &P) {
53675383
&isTransparent, &isSerialized, &isCanonical, &hasOwnershipSSA,
53685384
&isThunk, &isDynamic, &DynamicallyReplacedFunction,
53695385
&objCReplacementFor, &isGlobalInit, &inlineStrategy, &optimizationMode, nullptr,
5370-
&isWeakLinked, &isWithoutActuallyEscapingThunk, &Semantics,
5386+
&isWeakImported, &availability, &isWithoutActuallyEscapingThunk, &Semantics,
53715387
&SpecAttrs, &ClangDecl, &MRK, FunctionState, M) ||
53725388
P.parseToken(tok::at_sign, diag::expected_sil_function_name) ||
53735389
P.parseIdentifier(FnName, FnNameLoc, diag::expected_sil_function_name) ||
@@ -5401,7 +5417,8 @@ bool SILParserTUState::parseDeclSIL(Parser &P) {
54015417
if (!objCReplacementFor.empty())
54025418
FunctionState.F->setObjCReplacement(objCReplacementFor);
54035419
FunctionState.F->setGlobalInit(isGlobalInit);
5404-
FunctionState.F->setWeakLinked(isWeakLinked);
5420+
FunctionState.F->setAlwaysWeakImported(isWeakImported);
5421+
FunctionState.F->setAvailabilityForLinkage(availability);
54055422
FunctionState.F->setWithoutActuallyEscapingThunk(
54065423
isWithoutActuallyEscapingThunk);
54075424
FunctionState.F->setInlineStrategy(inlineStrategy);
@@ -5583,7 +5600,8 @@ bool SILParserTUState::parseSILGlobal(Parser &P) {
55835600
if (parseSILLinkage(GlobalLinkage, P) ||
55845601
parseDeclSILOptional(nullptr, &isSerialized, nullptr, nullptr, nullptr,
55855602
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
5586-
&isLet, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, State, M) ||
5603+
&isLet, nullptr, nullptr, nullptr, nullptr, nullptr,
5604+
nullptr, nullptr, State, M) ||
55875605
P.parseToken(tok::at_sign, diag::expected_sil_value_name) ||
55885606
P.parseIdentifier(GlobalName, NameLoc, diag::expected_sil_value_name) ||
55895607
P.parseToken(tok::colon, diag::expected_sil_type))
@@ -5630,7 +5648,7 @@ bool SILParserTUState::parseSILProperty(Parser &P) {
56305648

56315649
IsSerialized_t Serialized = IsNotSerialized;
56325650
if (parseDeclSILOptional(nullptr, &Serialized, nullptr, nullptr, nullptr,
5633-
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
5651+
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
56345652
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, SP, M))
56355653
return true;
56365654

@@ -5698,7 +5716,7 @@ bool SILParserTUState::parseSILVTable(Parser &P) {
56985716

56995717
IsSerialized_t Serialized = IsNotSerialized;
57005718
if (parseDeclSILOptional(nullptr, &Serialized, nullptr, nullptr, nullptr,
5701-
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
5719+
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
57025720
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
57035721
VTableState, M))
57045722
return true;
@@ -6234,7 +6252,7 @@ bool SILParserTUState::parseSILWitnessTable(Parser &P) {
62346252

62356253
IsSerialized_t isSerialized = IsNotSerialized;
62366254
if (parseDeclSILOptional(nullptr, &isSerialized, nullptr, nullptr, nullptr,
6237-
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
6255+
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
62386256
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
62396257
WitnessState, M))
62406258
return true;

lib/SIL/SILFunction.cpp

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,15 @@
1818
#include "swift/SIL/SILProfiler.h"
1919
#include "swift/SIL/CFG.h"
2020
#include "swift/SIL/PrettyStackTrace.h"
21+
#include "swift/AST/Availability.h"
2122
#include "swift/AST/GenericEnvironment.h"
23+
#include "swift/AST/Module.h"
2224
#include "swift/Basic/OptimizationMode.h"
2325
#include "swift/Basic/Statistic.h"
2426
#include "llvm/ADT/Optional.h"
2527
#include "llvm/Support/CommandLine.h"
2628
#include "llvm/Support/GraphWriter.h"
29+
#include "clang/AST/Decl.h"
2730

2831
using namespace swift;
2932
using namespace Lowering;
@@ -98,11 +101,13 @@ SILFunction::SILFunction(SILModule &Module, SILLinkage Linkage, StringRef Name,
98101
IsDynamicallyReplaceable_t isDynamic)
99102
: Module(Module), Name(Name), LoweredType(LoweredType),
100103
GenericEnv(genericEnv), SpecializationInfo(nullptr),
101-
DebugScope(DebugScope), EntryCount(entryCount), Bare(isBareSILFunction),
102-
Transparent(isTrans), Serialized(isSerialized), Thunk(isThunk),
104+
DebugScope(DebugScope), EntryCount(entryCount),
105+
Availability(AvailabilityContext::alwaysAvailable()),
106+
Bare(isBareSILFunction), Transparent(isTrans),
107+
Serialized(isSerialized), Thunk(isThunk),
103108
ClassSubclassScope(unsigned(classSubclassScope)), GlobalInitFlag(false),
104109
InlineStrategy(inlineStrategy), Linkage(unsigned(Linkage)),
105-
HasCReferences(false), IsWeakLinked(false),
110+
HasCReferences(false), IsWeakImported(false),
106111
IsDynamicReplaceable(isDynamic),
107112
Inlined(false), Zombie(false), HasOwnership(true),
108113
WasDeserializedCanonical(false), IsWithoutActuallyEscapingThunk(false),
@@ -272,6 +277,27 @@ bool SILFunction::isTypeABIAccessible(SILType type) const {
272277
ResilienceExpansion::Minimal);
273278
}
274279

280+
bool SILFunction::isWeakImported() const {
281+
// For imported functions check the Clang declaration.
282+
if (ClangNodeOwner)
283+
return ClangNodeOwner->getClangDecl()->isWeakImported();
284+
285+
// For native functions check a flag on the SILFunction
286+
// itself.
287+
if (!isAvailableExternally())
288+
return false;
289+
290+
if (isAlwaysWeakImported())
291+
return true;
292+
293+
if (Availability.isAlwaysAvailable())
294+
return false;
295+
296+
auto fromContext = AvailabilityContext::forDeploymentTarget(
297+
getASTContext());
298+
return !fromContext.isContainedIn(Availability);
299+
}
300+
275301
SILBasicBlock *SILFunction::createBasicBlock() {
276302
return new (getModule()) SILBasicBlock(this, nullptr, false);
277303
}

lib/SIL/SILFunctionBuilder.cpp

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -159,17 +159,8 @@ SILFunctionBuilder::getOrCreateFunction(SILLocation loc, SILDeclRef constant,
159159
if (constant.isForeign && decl->hasClangNode())
160160
F->setClangNodeOwner(decl);
161161

162-
if (decl->isAlwaysWeakImported())
163-
F->setWeakLinked();
164-
else {
165-
auto containingContext = decl->getAvailabilityForLinkage();
166-
if (!containingContext.isAlwaysAvailable()) {
167-
auto fromContext = AvailabilityContext::forDeploymentTarget(
168-
decl->getASTContext());
169-
if (!fromContext.isContainedIn(containingContext))
170-
F->setWeakLinked();
171-
}
172-
}
162+
F->setAvailabilityForLinkage(decl->getAvailabilityForLinkage());
163+
F->setAlwaysWeakImported(decl->isAlwaysWeakImported());
173164

174165
if (auto *accessor = dyn_cast<AccessorDecl>(decl)) {
175166
auto *storage = accessor->getStorage();

lib/SIL/SILPrinter.cpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2322,8 +2322,13 @@ void SILFunction::print(SILPrintContext &PrintCtx) const {
23222322

23232323
if (isGlobalInit())
23242324
OS << "[global_init] ";
2325-
if (isWeakLinked())
2326-
OS << "[_weakLinked] ";
2325+
if (isAlwaysWeakImported())
2326+
OS << "[weak_imported] ";
2327+
auto availability = getAvailabilityForLinkage();
2328+
if (!availability.isAlwaysAvailable()) {
2329+
auto version = availability.getOSVersion().getLowerEndpoint();
2330+
OS << "[available " << version.getAsString() << "] ";
2331+
}
23272332

23282333
switch (getInlineStrategy()) {
23292334
case NoInline: OS << "[noinline] "; break;

0 commit comments

Comments
 (0)