Skip to content

Commit

Permalink
SIL: Serialize availability with the SIL function, not just a weak im…
Browse files Browse the repository at this point in the history
…ported 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>.
  • Loading branch information
slavapestov committed Sep 7, 2019
1 parent a0e160e commit 3a59e7c
Show file tree
Hide file tree
Showing 21 changed files with 294 additions and 112 deletions.
2 changes: 2 additions & 0 deletions include/swift/AST/DiagnosticsParse.def
Original file line number Diff line number Diff line change
Expand Up @@ -640,6 +640,8 @@ ERROR(expected_sil_function_type, none,
"sil function expected to have SIL function type", ())
ERROR(sil_dynamically_replaced_func_not_found,none,
"dynamically replaced function not found %0", (Identifier))
ERROR(sil_availability_expected_version,none,
"expected version number in 'available' attribute", ())

// SIL Stage
ERROR(expected_sil_stage_name, none,
Expand Down
32 changes: 24 additions & 8 deletions include/swift/SIL/SILFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#define SWIFT_SIL_SILFUNCTION_H

#include "swift/AST/ASTNode.h"
#include "swift/AST/Availability.h"
#include "swift/AST/ResilienceExpansion.h"
#include "swift/Basic/ProfileCounter.h"
#include "swift/SIL/SILBasicBlock.h"
Expand Down Expand Up @@ -178,6 +179,10 @@ class SILFunction
/// Contains Function Entry Count
ProfileCounter EntryCount;

/// The availability used to determine if declarations of this function
/// should use weak linking.
AvailabilityContext Availability;

/// This is the number of uses of this SILFunction inside the SIL.
/// It does not include references from debug scopes.
unsigned RefCount = 0;
Expand Down Expand Up @@ -216,8 +221,9 @@ class SILFunction
/// would indicate.
unsigned HasCReferences : 1;

/// Whether cross-module references to this function should use weak linking.
unsigned IsWeakLinked : 1;
/// Whether cross-module references to this function should always use
/// weak linking.
unsigned IsWeakImported : 1;

/// Whether the implementation can be dynamically replaced.
unsigned IsDynamicReplaceable : 1;
Expand Down Expand Up @@ -591,16 +597,26 @@ class SILFunction
bool hasCReferences() const { return HasCReferences; }
void setHasCReferences(bool value) { HasCReferences = value; }

/// Returns the availability context used to determine if the function's
/// symbol should be weakly referenced across module boundaries.
AvailabilityContext getAvailabilityForLinkage() const {
return Availability;
}

void setAvailabilityForLinkage(AvailabilityContext availability) {
Availability = availability;
}

/// Returns whether this function's symbol must always be weakly referenced
/// across module boundaries.
bool isWeakLinked() const { return IsWeakLinked; }
/// Forces IRGen to treat references to this function as weak across module
/// boundaries (i.e. if it has external linkage).
void setWeakLinked(bool value = true) {
assert(!IsWeakLinked && "already set");
IsWeakLinked = value;
bool isAlwaysWeakImported() const { return IsWeakImported; }

void setAlwaysWeakImported(bool value) {
IsWeakImported = value;
}

bool isWeakImported() const;

/// Returns whether this function implementation can be dynamically replaced.
IsDynamicallyReplaceable_t isDynamicallyReplaceable() const {
return IsDynamicallyReplaceable_t(IsDynamicReplaceable);
Expand Down
14 changes: 2 additions & 12 deletions lib/IRGen/Linking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
#include "IRGenMangler.h"
#include "IRGenModule.h"
#include "swift/AST/ASTMangler.h"
#include "swift/AST/Availability.h"
#include "swift/AST/IRGenOptions.h"
#include "swift/ClangImporter/ClangModule.h"
#include "swift/SIL/SILGlobalVariable.h"
Expand Down Expand Up @@ -966,22 +965,13 @@ bool LinkEntity::isWeakImported(ModuleDecl *module) const {
switch (getKind()) {
case Kind::SILGlobalVariable:
if (getSILGlobalVariable()->getDecl()) {
return getSILGlobalVariable()->getDecl()
->isWeakImported(module);
return getSILGlobalVariable()->getDecl()->isWeakImported(module);
}
return false;
case Kind::DynamicallyReplaceableFunctionKey:
case Kind::DynamicallyReplaceableFunctionVariable:
case Kind::SILFunction: {
// For imported functions check the Clang declaration.
if (auto clangOwner = getSILFunction()->getClangNodeOwner())
return clangOwner->isWeakImported(module);

// For native functions check a flag on the SILFunction
// itself.
if (getSILFunction()->isWeakLinked())
return getSILFunction()->isAvailableExternally();
return false;
return getSILFunction()->isWeakImported();
}

case Kind::AssociatedConformanceDescriptor:
Expand Down
39 changes: 28 additions & 11 deletions lib/ParseSIL/ParseSIL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -985,7 +985,9 @@ static bool parseDeclSILOptional(bool *isTransparent,
bool *isGlobalInit,
Inline_t *inlineStrategy,
OptimizationMode *optimizationMode,
bool *isLet, bool *isWeakLinked,
bool *isLet,
bool *isWeakImported,
AvailabilityContext *availability,
bool *isWithoutActuallyEscapingThunk,
SmallVectorImpl<std::string> *Semantics,
SmallVectorImpl<ParsedSpecAttr> *SpecAttrs,
Expand Down Expand Up @@ -1027,14 +1029,27 @@ static bool parseDeclSILOptional(bool *isTransparent,
*isWithoutActuallyEscapingThunk = true;
else if (isGlobalInit && SP.P.Tok.getText() == "global_init")
*isGlobalInit = true;
else if (isWeakLinked && SP.P.Tok.getText() == "_weakLinked")
else if (isWeakImported && SP.P.Tok.getText() == "weak_imported") {
if (M.getASTContext().LangOpts.Target.isOSBinFormatCOFF())
SP.P.diagnose(SP.P.Tok, diag::attr_unsupported_on_target,
SP.P.Tok.getText(),
M.getASTContext().LangOpts.Target.str());
else
*isWeakLinked = true;
else if (inlineStrategy && SP.P.Tok.getText() == "noinline")
*isWeakImported = true;
} else if (availability && SP.P.Tok.getText() == "available") {
SP.P.consumeToken(tok::identifier);

SourceRange range;
llvm::VersionTuple version;
if (SP.P.parseVersionTuple(version, range,
diag::sil_availability_expected_version))
return true;

*availability = AvailabilityContext(VersionRange::allGTE(version));

SP.P.parseToken(tok::r_square, diag::expected_in_attribute_list);
continue;
} else if (inlineStrategy && SP.P.Tok.getText() == "noinline")
*inlineStrategy = NoInline;
else if (optimizationMode && SP.P.Tok.getText() == "Onone")
*optimizationMode = OptimizationMode::NoOptimization;
Expand Down Expand Up @@ -5402,7 +5417,8 @@ bool SILParserTUState::parseDeclSIL(Parser &P) {
IsExactSelfClass_t isExactSelfClass = IsNotExactSelfClass;
bool hasOwnershipSSA = false;
IsThunk_t isThunk = IsNotThunk;
bool isGlobalInit = false, isWeakLinked = false;
bool isGlobalInit = false, isWeakImported = false;
AvailabilityContext availability = AvailabilityContext::alwaysAvailable();
bool isWithoutActuallyEscapingThunk = false;
Inline_t inlineStrategy = InlineDefault;
OptimizationMode optimizationMode = OptimizationMode::NotSet;
Expand All @@ -5417,7 +5433,7 @@ bool SILParserTUState::parseDeclSIL(Parser &P) {
&isTransparent, &isSerialized, &isCanonical, &hasOwnershipSSA,
&isThunk, &isDynamic, &isExactSelfClass, &DynamicallyReplacedFunction,
&objCReplacementFor, &isGlobalInit, &inlineStrategy, &optimizationMode, nullptr,
&isWeakLinked, &isWithoutActuallyEscapingThunk, &Semantics,
&isWeakImported, &availability, &isWithoutActuallyEscapingThunk, &Semantics,
&SpecAttrs, &ClangDecl, &MRK, FunctionState, M) ||
P.parseToken(tok::at_sign, diag::expected_sil_function_name) ||
P.parseIdentifier(FnName, FnNameLoc, diag::expected_sil_function_name) ||
Expand Down Expand Up @@ -5452,7 +5468,8 @@ bool SILParserTUState::parseDeclSIL(Parser &P) {
if (!objCReplacementFor.empty())
FunctionState.F->setObjCReplacement(objCReplacementFor);
FunctionState.F->setGlobalInit(isGlobalInit);
FunctionState.F->setWeakLinked(isWeakLinked);
FunctionState.F->setAlwaysWeakImported(isWeakImported);
FunctionState.F->setAvailabilityForLinkage(availability);
FunctionState.F->setWithoutActuallyEscapingThunk(
isWithoutActuallyEscapingThunk);
FunctionState.F->setInlineStrategy(inlineStrategy);
Expand Down Expand Up @@ -5642,7 +5659,7 @@ bool SILParserTUState::parseSILGlobal(Parser &P) {
nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr,
&isLet, nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, State, M) ||
nullptr, nullptr, State, M) ||
P.parseToken(tok::at_sign, diag::expected_sil_value_name) ||
P.parseIdentifier(GlobalName, NameLoc, diag::expected_sil_value_name) ||
P.parseToken(tok::colon, diag::expected_sil_type))
Expand Down Expand Up @@ -5691,7 +5708,7 @@ bool SILParserTUState::parseSILProperty(Parser &P) {
if (parseDeclSILOptional(nullptr, &Serialized, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, SP, M))
nullptr, nullptr, nullptr, SP, M))
return true;

ValueDecl *VD;
Expand Down Expand Up @@ -5760,7 +5777,7 @@ bool SILParserTUState::parseSILVTable(Parser &P) {
if (parseDeclSILOptional(nullptr, &Serialized, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr,
nullptr, nullptr, nullptr,
VTableState, M))
return true;

Expand Down Expand Up @@ -6295,7 +6312,7 @@ bool SILParserTUState::parseSILWitnessTable(Parser &P) {
if (parseDeclSILOptional(nullptr, &isSerialized, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr,
nullptr, nullptr, nullptr,
WitnessState, M))
return true;

Expand Down
30 changes: 28 additions & 2 deletions lib/SIL/SILFunction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,15 @@
#include "swift/SIL/SILProfiler.h"
#include "swift/SIL/CFG.h"
#include "swift/SIL/PrettyStackTrace.h"
#include "swift/AST/Availability.h"
#include "swift/AST/GenericEnvironment.h"
#include "swift/AST/Module.h"
#include "swift/Basic/OptimizationMode.h"
#include "swift/Basic/Statistic.h"
#include "llvm/ADT/Optional.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/GraphWriter.h"
#include "clang/AST/Decl.h"

using namespace swift;
using namespace Lowering;
Expand Down Expand Up @@ -93,11 +96,13 @@ SILFunction::SILFunction(SILModule &Module, SILLinkage Linkage, StringRef Name,
IsExactSelfClass_t isExactSelfClass)
: Module(Module), Name(Name), LoweredType(LoweredType),
GenericEnv(genericEnv), SpecializationInfo(nullptr),
EntryCount(entryCount), Bare(isBareSILFunction), Transparent(isTrans),
EntryCount(entryCount),
Availability(AvailabilityContext::alwaysAvailable()),
Bare(isBareSILFunction), Transparent(isTrans),
Serialized(isSerialized), Thunk(isThunk),
ClassSubclassScope(unsigned(classSubclassScope)), GlobalInitFlag(false),
InlineStrategy(inlineStrategy), Linkage(unsigned(Linkage)),
HasCReferences(false), IsWeakLinked(false),
HasCReferences(false), IsWeakImported(false),
IsDynamicReplaceable(isDynamic),
ExactSelfClass(isExactSelfClass),
Inlined(false), Zombie(false), HasOwnership(true),
Expand Down Expand Up @@ -259,6 +264,27 @@ bool SILFunction::isTypeABIAccessible(SILType type) const {
return getModule().isTypeABIAccessible(type, getResilienceExpansion());
}

bool SILFunction::isWeakImported() const {
// For imported functions check the Clang declaration.
if (ClangNodeOwner)
return ClangNodeOwner->getClangDecl()->isWeakImported();

// For native functions check a flag on the SILFunction
// itself.
if (!isAvailableExternally())
return false;

if (isAlwaysWeakImported())
return true;

if (Availability.isAlwaysAvailable())
return false;

auto fromContext = AvailabilityContext::forDeploymentTarget(
getASTContext());
return !fromContext.isContainedIn(Availability);
}

SILBasicBlock *SILFunction::createBasicBlock() {
return new (getModule()) SILBasicBlock(this, nullptr, false);
}
Expand Down
13 changes: 2 additions & 11 deletions lib/SIL/SILFunctionBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -162,17 +162,8 @@ SILFunctionBuilder::getOrCreateFunction(SILLocation loc, SILDeclRef constant,
if (constant.isForeign && decl->hasClangNode())
F->setClangNodeOwner(decl);

if (decl->isAlwaysWeakImported())
F->setWeakLinked();
else {
auto containingContext = decl->getAvailabilityForLinkage();
if (!containingContext.isAlwaysAvailable()) {
auto fromContext = AvailabilityContext::forDeploymentTarget(
decl->getASTContext());
if (!fromContext.isContainedIn(containingContext))
F->setWeakLinked();
}
}
F->setAvailabilityForLinkage(decl->getAvailabilityForLinkage());
F->setAlwaysWeakImported(decl->isAlwaysWeakImported());

if (auto *accessor = dyn_cast<AccessorDecl>(decl)) {
auto *storage = accessor->getStorage();
Expand Down
9 changes: 7 additions & 2 deletions lib/SIL/SILPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2332,8 +2332,13 @@ void SILFunction::print(SILPrintContext &PrintCtx) const {

if (isGlobalInit())
OS << "[global_init] ";
if (isWeakLinked())
OS << "[_weakLinked] ";
if (isAlwaysWeakImported())
OS << "[weak_imported] ";
auto availability = getAvailabilityForLinkage();
if (!availability.isAlwaysAvailable()) {
auto version = availability.getOSVersion().getLowerEndpoint();
OS << "[available " << version.getAsString() << "] ";
}

switch (getInlineStrategy()) {
case NoInline: OS << "[noinline] "; break;
Expand Down
16 changes: 0 additions & 16 deletions lib/Serialization/Deserialization.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3872,18 +3872,6 @@ llvm::Error DeclDeserializer::deserializeDeclAttributes() {
}

case decls_block::Available_DECL_ATTR: {
#define LIST_VER_TUPLE_PIECES(X)\
X##_Major, X##_Minor, X##_Subminor, X##_HasMinor, X##_HasSubminor
#define DEF_VER_TUPLE_PIECES(X) unsigned LIST_VER_TUPLE_PIECES(X)
#define DECODE_VER_TUPLE(X)\
if (X##_HasMinor) {\
if (X##_HasSubminor)\
X = llvm::VersionTuple(X##_Major, X##_Minor, X##_Subminor);\
else\
X = llvm::VersionTuple(X##_Major, X##_Minor);\
}\
else X = llvm::VersionTuple(X##_Major);

bool isImplicit;
bool isUnavailable;
bool isDeprecated;
Expand Down Expand Up @@ -3932,10 +3920,6 @@ llvm::Error DeclDeserializer::deserializeDeclAttributes() {
Obsoleted, SourceRange(),
platformAgnostic, isImplicit);
break;

#undef DEF_VER_TUPLE_PIECES
#undef LIST_VER_TUPLE_PIECES
#undef DECODE_VER_TUPLE
}

case decls_block::ObjC_DECL_ATTR: {
Expand Down
22 changes: 17 additions & 5 deletions lib/Serialization/DeserializeSIL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -469,13 +469,15 @@ SILDeserializer::readSILFunctionChecked(DeclID FID, SILFunction *existingFn,
unsigned rawLinkage, isTransparent, isSerialized, isThunk,
isWithoutactuallyEscapingThunk, isGlobal, inlineStrategy,
optimizationMode, effect, numSpecAttrs, hasQualifiedOwnership,
isWeakLinked, isDynamic, isExactSelfClass;
isWeakImported, LIST_VER_TUPLE_PIECES(available),
isDynamic, isExactSelfClass;
ArrayRef<uint64_t> SemanticsIDs;
SILFunctionLayout::readRecord(
scratch, rawLinkage, isTransparent, isSerialized, isThunk,
isWithoutactuallyEscapingThunk, isGlobal, inlineStrategy,
optimizationMode, effect, numSpecAttrs, hasQualifiedOwnership,
isWeakLinked, isDynamic, isExactSelfClass,
isWeakImported, LIST_VER_TUPLE_PIECES(available),
isDynamic, isExactSelfClass,
funcTyID, replacedFunctionID, genericSigID,
clangNodeOwnerID, SemanticsIDs);

Expand Down Expand Up @@ -580,7 +582,15 @@ SILDeserializer::readSILFunctionChecked(DeclID FID, SILFunction *existingFn,
fn->setGlobalInit(isGlobal == 1);
fn->setEffectsKind(EffectsKind(effect));
fn->setOptimizationMode(OptimizationMode(optimizationMode));
fn->setWeakLinked(isWeakLinked);
fn->setAlwaysWeakImported(isWeakImported);

llvm::VersionTuple available;
DECODE_VER_TUPLE(available);
fn->setAvailabilityForLinkage(
available.empty()
? AvailabilityContext::alwaysAvailable()
: AvailabilityContext(VersionRange::allGTE(available)));

fn->setIsDynamic(IsDynamicallyReplaceable_t(isDynamic));
fn->setIsExactSelfClass(IsExactSelfClass_t(isExactSelfClass));
if (replacedFunction)
Expand Down Expand Up @@ -2539,13 +2549,15 @@ bool SILDeserializer::hasSILFunction(StringRef Name,
unsigned rawLinkage, isTransparent, isSerialized, isThunk,
isWithoutactuallyEscapingThunk, isGlobal, inlineStrategy,
optimizationMode, effect, numSpecAttrs, hasQualifiedOwnership,
isWeakLinked, isDynamic, isExactSelfClass;
isWeakImported, LIST_VER_TUPLE_PIECES(available),
isDynamic, isExactSelfClass;
ArrayRef<uint64_t> SemanticsIDs;
SILFunctionLayout::readRecord(
scratch, rawLinkage, isTransparent, isSerialized, isThunk,
isWithoutactuallyEscapingThunk, isGlobal, inlineStrategy,
optimizationMode, effect, numSpecAttrs, hasQualifiedOwnership,
isWeakLinked, isDynamic, isExactSelfClass,
isWeakImported, LIST_VER_TUPLE_PIECES(available),
isDynamic, isExactSelfClass,
funcTyID, replacedFunctionID, genericSigID,
clangOwnerID, SemanticsIDs);
auto linkage = fromStableSILLinkage(rawLinkage);
Expand Down
Loading

0 comments on commit 3a59e7c

Please sign in to comment.