Skip to content

stdlib: Add backward deployment versions for the dynamic-replacement runtime functions #25473

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
1 change: 1 addition & 0 deletions cmake/modules/SwiftSource.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,7 @@ function(_compile_swift_files
# into the new runtime.
if (SWIFTFILE_IS_STDLIB OR SWIFTFILE_IS_SDK_OVERLAY)
list(APPEND swift_flags "-runtime-compatibility-version" "none")
list(APPEND swift_flags "-disable-autolinking-runtime-compatibility-dynamic-replacements")
endif()

if (SWIFTFILE_IS_STDLIB_CORE OR SWIFTFILE_IS_SDK_OVERLAY)
Expand Down
4 changes: 4 additions & 0 deletions include/swift/AST/ASTContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -590,6 +590,10 @@ class ASTContext final {
/// Get the runtime availability of the opaque types language feature for the target platform.
AvailabilityContext getOpaqueTypeAvailability();

/// Get the runtime availability of features introduced in the Swift 5.1
/// compiler for the target platform.
AvailabilityContext getSwift51Availability();

//===--------------------------------------------------------------------===//
// Diagnostics Helper functions
//===--------------------------------------------------------------------===//
Expand Down
1 change: 1 addition & 0 deletions include/swift/AST/IRGenOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,7 @@ class IRGenOptions {

/// Pull in runtime compatibility shim libraries by autolinking.
Optional<llvm::VersionTuple> AutolinkRuntimeCompatibilityLibraryVersion;
Optional<llvm::VersionTuple> AutolinkRuntimeCompatibilityDynamicReplacementLibraryVersion;

IRGenOptions()
: DWARFVersion(2), OutputKind(IRGenOutputKind::LLVMAssembly),
Expand Down
6 changes: 6 additions & 0 deletions include/swift/Option/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -904,4 +904,10 @@ def disable_autolinking_runtime_compatibility : Flag<["-"], "disable-autolinking
Flags<[FrontendOption]>,
HelpText<"Do not use autolinking for runtime compatibility libraries">;

def disable_autolinking_runtime_compatibility_dynamic_replacements
: Flag<[ "-" ], "disable-autolinking-runtime-compatibility-dynamic-replacements">,
Flags<[ FrontendOption ]>,
HelpText<"Do not use autolinking for the dynamic replacement runtime "
"compatibility library">;

include "FrontendOptions.td"
6 changes: 6 additions & 0 deletions include/swift/Runtime/Exclusivity.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,19 @@ void swift_beginAccess(void *pointer, ValueBuffer *buffer,
/// replacement function if it should be called.
/// Returns null if the original function (which is passed in \p CurrFn) should
/// be called.
#ifdef __APPLE__
__attribute__((weak_import))
#endif
Copy link
Contributor

@jckarter jckarter Jun 14, 2019

Choose a reason for hiding this comment

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

This will export these symbols as a weak definition, which is probably not what you want (and very bad for load time performance). Did you mean __attribute__((weak_import)) ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes. Will fix.

SWIFT_RUNTIME_EXPORT
char *swift_getFunctionReplacement(char **ReplFnPtr, char *CurrFn);

/// Returns the original function of a replaced function, which is loaded from
/// \p OrigFnPtr.
/// This function is called from a replacement function to call the original
/// function.
#ifdef __APPLE__
__attribute__((weak_import))
#endif
SWIFT_RUNTIME_EXPORT
char *swift_getOrigOfReplaceable(char **OrigFnPtr);

Expand Down
10 changes: 9 additions & 1 deletion include/swift/Runtime/RuntimeFnWrappersGen.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,13 @@
namespace swift {

class AvailabilityContext;
class ASTContext;

enum class RuntimeAvailability {
AlwaysAvailable,
AvailableByCompatibilityLibrary,
ConditionallyAvailable
};

/// Generate an llvm declaration for a runtime entry with a
/// given name, return types, argument types, attributes and
Expand All @@ -30,7 +37,8 @@ llvm::Constant *getRuntimeFn(llvm::Module &Module,
llvm::Constant *&cache,
char const *name,
llvm::CallingConv::ID cc,
bool isWeakLinked,
RuntimeAvailability availability,
ASTContext *context,
llvm::ArrayRef<llvm::Type*> retTypes,
llvm::ArrayRef<llvm::Type*> argTypes,
llvm::ArrayRef<llvm::Attribute::AttrKind> attrs);
Expand Down
10 changes: 6 additions & 4 deletions include/swift/Runtime/RuntimeFunctions.def
Original file line number Diff line number Diff line change
Expand Up @@ -638,7 +638,7 @@ FUNCTION(GetGenericMetadata, swift_getGenericMetadata,
// const OpaqueTypeDescriptor *descriptor,
// uintptr_t index);
FUNCTION(GetOpaqueTypeMetadata, swift_getOpaqueTypeMetadata,
SwiftCC, OpaqueTypeAvailability,
SwiftCC, ConditionallyAvailable,
Copy link
Contributor

@jckarter jckarter Jun 17, 2019

Choose a reason for hiding this comment

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

These all still need to call a function to return the availability appropriate for the current deployment target. There will be runtime entry points in the future that are available on a different set of OSes from Swift 5.1, so we shouldn't hardcode that.

Copy link
Contributor Author

@aschwaighofer aschwaighofer Jun 17, 2019

Choose a reason for hiding this comment

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

This is what conditionally available means?

The code in getRuntimeFn() queries whether the feature is available based on whether something is AlwaysAvailable| ConditionallyAvailable | AvailableByCompatibilityLibrary:

  auto isFeatureAvailable = [&]() -> bool {
     auto deploymentAvailability =
         AvailabilityContext::forDeploymentTarget(*context);
     auto featureAvailability = context->getSwift51Availability();
     return deploymentAvailability.isContainedIn(featureAvailability);
   };

  switch (availability) {
   case RuntimeAvailability:: AlwaysAvailable:
     // Nothing to do.
     break;
   case RuntimeAvailability::ConditionallyAvailable: {
     isWeakLinked = !isFeatureAvailable();
     break;
   }
   case RuntimeAvailability::AvailableByCompatibilityLibrary: {
     if (!isFeatureAvailable())
       functionName.append("50");
     break;
   }
   }

In the future we can extend this by adding another entry to runtime.def that specifies at which swift version things became available ...

FUNCTION(GetOpaqueTypeMetadata, swift_getOpaqueTypeMetadata,
         SwiftCC, ConditionallyAvailable, Swift51

And then generate the availability based on that.

 context->get##AVAILVERSION##Availability

Copy link
Contributor

@jckarter jckarter Jun 17, 2019

Choose a reason for hiding this comment

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

An entry point is only ConditionallyAvailable if we're deploying back to a runtime version that may not have the entry point. If we're deploying to an OS with Swift 5.1, everything is AlwaysAvailable. Having a single version doesn't seem sufficient to me, because in the future, we may end up in a situation where a runtime entry point can only be back-deployed in the compatibility library to certain runtime versions; for instance, a Swift 5.2 feature may be completely unavailable on Swift 5.0, although we have a compatibility fallback that works for the 5.1 runtime. The most future-proof thing to do is to have the FUNCTION xmacro refer to a function that returns the enum value appropriate to the deployment target, the way it was before.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I will address this in a follow-up patch.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Attempt of follow-up here: #25573

RETURNS(TypeMetadataResponseTy),
ARGS(SizeTy, Int8PtrTy, OpaqueTypeDescriptorPtrTy, SizeTy),
ATTRS(NoUnwind, ReadOnly))
Expand All @@ -647,7 +647,7 @@ FUNCTION(GetOpaqueTypeMetadata, swift_getOpaqueTypeMetadata,
// const OpaqueTypeDescriptor *descriptor,
// uintptr_t index);
FUNCTION(GetOpaqueTypeConformance, swift_getOpaqueTypeConformance,
SwiftCC, OpaqueTypeAvailability,
SwiftCC, ConditionallyAvailable,
RETURNS(WitnessTablePtrTy),
ARGS(Int8PtrTy, OpaqueTypeDescriptorPtrTy, SizeTy),
ATTRS(NoUnwind, ReadOnly))
Expand Down Expand Up @@ -1225,12 +1225,14 @@ FUNCTION(EndAccess, swift_endAccess, C_CC, AlwaysAvailable,
ARGS(getFixedBufferTy()->getPointerTo()),
ATTRS(NoUnwind))

FUNCTION(GetOrigOfReplaceable, swift_getOrigOfReplaceable, C_CC, AlwaysAvailable,
FUNCTION(GetOrigOfReplaceable, swift_getOrigOfReplaceable, C_CC,
AvailableByCompatibilityLibrary,
RETURNS(FunctionPtrTy),
ARGS(FunctionPtrTy->getPointerTo()),
ATTRS(NoUnwind))

FUNCTION(GetReplacement, swift_getFunctionReplacement, C_CC, AlwaysAvailable,
FUNCTION(GetReplacement, swift_getFunctionReplacement, C_CC,
AvailableByCompatibilityLibrary,
RETURNS(FunctionPtrTy),
ARGS(FunctionPtrTy->getPointerTo(), FunctionPtrTy),
ATTRS(NoUnwind))
Expand Down
4 changes: 4 additions & 0 deletions lib/AST/Availability.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,10 @@ AvailabilityContext AvailabilityInference::inferForType(Type t) {
}

AvailabilityContext ASTContext::getOpaqueTypeAvailability() {
return getSwift51Availability();
}

AvailabilityContext ASTContext::getSwift51Availability() {
auto target = LangOpts.Target;

if (target.isMacOSX()) {
Expand Down
16 changes: 16 additions & 0 deletions lib/Driver/DarwinToolChains.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,22 @@ toolchains::Darwin::constructInvocation(const LinkJobAction &job,
}
}

if (job.getKind() == LinkKind::Executable) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Er, you don't need this part anymore, right? And you never wanted it to just be Executables-linked-with-swiftc anyway.

Copy link
Contributor

Choose a reason for hiding this comment

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

We still ultimately want this to only be linked into executables. We were going to handle that as a followup. For these specific backfill entry points, they're small enough that burning them into dylibs and having potentially-mismatching versions in different images is probably OK, but that's not generally a situation we want to be in.

Copy link
Contributor

Choose a reason for hiding this comment

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

I don't understand that part. We can't rely on executables having any Swift in them, and it's unlikely that Xcode will switch to linking with swiftc anyway. The autolinking part for dynamic replacement isn't going away.

Copy link
Contributor

Choose a reason for hiding this comment

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

(I do want a resolution for this because it's interfering with me rebasing #24787.)

Copy link
Contributor

Choose a reason for hiding this comment

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

If there's any Swift in the process, then the executable is the only useful place to put backward-compatibility fills, so much like arclite, we might want eventually for Xcode to link this in unconditionally because a non-Swift executable could be using dylibs with Swift in them.

if (runtimeCompatibilityVersion)
if (*runtimeCompatibilityVersion <= llvm::VersionTuple(5, 0)) {
// Swift 5.0 dynamic replacement compatibility library.
SmallString<128> BackDeployLib;
BackDeployLib.append(RuntimeLibPath);
llvm::sys::path::append(BackDeployLib,
"libswiftCompatibilityDynamicReplacements.a");

if (llvm::sys::fs::exists(BackDeployLib)) {
Arguments.push_back("-force_load");
Arguments.push_back(context.Args.MakeArgString(BackDeployLib));
}
}
}

// Link the standard library.
Arguments.push_back("-L");
if (context.Args.hasFlag(options::OPT_static_stdlib,
Expand Down
7 changes: 6 additions & 1 deletion lib/Driver/ToolChains.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -445,7 +445,12 @@ ToolChain::constructInvocation(const CompileJobAction &job,
Arguments.push_back("-runtime-compatibility-version");
Arguments.push_back(arg->getValue());
}


context.Args.AddLastArg(
Arguments,
options::
OPT_disable_autolinking_runtime_compatibility_dynamic_replacements);

return II;
}

Expand Down
5 changes: 5 additions & 0 deletions lib/Frontend/CompilerInvocation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1178,6 +1178,11 @@ static bool ParseIRGenArgs(IRGenOptions &Opts, ArgList &Args,
runtimeCompatibilityVersion;
}

if (!Args.hasArg(options::
OPT_disable_autolinking_runtime_compatibility_dynamic_replacements)) {
Opts.AutolinkRuntimeCompatibilityDynamicReplacementLibraryVersion =
getSwiftRuntimeCompatibilityVersionForTarget(Triple);
}
return false;
}

Expand Down
18 changes: 14 additions & 4 deletions lib/IRGen/GenDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -468,6 +468,15 @@ void IRGenModule::emitSourceFile(SourceFile &SF) {
/*forceLoad*/ true));
}
}

if (auto compatibilityVersion =
IRGen.Opts.AutolinkRuntimeCompatibilityDynamicReplacementLibraryVersion) {
if (*compatibilityVersion <= llvm::VersionTuple(5, 0)) {
this->addLinkLibrary(LinkLibrary("swiftCompatibilityDynamicReplacements",
LibraryKind::Library,
/*forceLoad*/ true));
}
}
}
}

Expand Down Expand Up @@ -2239,8 +2248,8 @@ void IRGenModule::createReplaceableProlog(IRGenFunction &IGF, SILFunction *f) {
rhs = FnAddr;
} else {
// Call swift_getFunctionReplacement to check which function to call.
auto *callRTFunc = IGF.Builder.CreateCall(getGetReplacementFn(),
{ ReplAddr, FnAddr });
auto *callRTFunc =
IGF.Builder.CreateCall(getGetReplacementFn(), {ReplAddr, FnAddr});
callRTFunc->setDoesNotThrow();
ReplFn = callRTFunc;
rhs = llvm::ConstantExpr::getNullValue(ReplFn->getType());
Expand Down Expand Up @@ -2408,8 +2417,9 @@ void IRGenModule::emitDynamicReplacementOriginalFunctionThunk(SILFunction *f) {
llvm::ConstantExpr::getInBoundsGetElementPtr(nullptr, linkEntry, indices),
FunctionPtrTy->getPointerTo());

auto *OrigFn = IGF.Builder.CreateCall(getGetOrigOfReplaceableFn(),
{ fnPtrAddr });
auto *OrigFn =
IGF.Builder.CreateCall(getGetOrigOfReplaceableFn(), {fnPtrAddr});

OrigFn->setDoesNotThrow();

auto *typeFnPtr =
Expand Down
53 changes: 37 additions & 16 deletions lib/IRGen/IRGenModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -539,18 +539,12 @@ namespace RuntimeConstants {
const auto NoUnwind = llvm::Attribute::NoUnwind;
const auto ZExt = llvm::Attribute::ZExt;
const auto FirstParamReturned = llvm::Attribute::Returned;

bool AlwaysAvailable(ASTContext &Context) {
return false;
}

bool OpaqueTypeAvailability(ASTContext &Context) {
auto deploymentAvailability =
AvailabilityContext::forDeploymentTarget(Context);
auto featureAvailability = Context.getOpaqueTypeAvailability();

return !deploymentAvailability.isContainedIn(featureAvailability);
}

const auto AlwaysAvailable = RuntimeAvailability::AlwaysAvailable;
const auto AvailableByCompatibilityLibrary =
RuntimeAvailability::AvailableByCompatibilityLibrary;
const auto ConditionallyAvailable =
RuntimeAvailability::ConditionallyAvailable;
} // namespace RuntimeConstants

// We don't use enough attributes to justify generalizing the
Expand Down Expand Up @@ -594,13 +588,40 @@ llvm::Constant *swift::getRuntimeFn(llvm::Module &Module,
llvm::Constant *&cache,
const char *name,
llvm::CallingConv::ID cc,
bool isWeakLinked,
RuntimeAvailability availability,
ASTContext *context,
llvm::ArrayRef<llvm::Type*> retTypes,
llvm::ArrayRef<llvm::Type*> argTypes,
ArrayRef<Attribute::AttrKind> attrs) {

if (cache)
return cache;


bool isWeakLinked = false;
std::string functionName(name);

auto isFeatureAvailable = [&]() -> bool {
auto deploymentAvailability =
AvailabilityContext::forDeploymentTarget(*context);
auto featureAvailability = context->getSwift51Availability();
return deploymentAvailability.isContainedIn(featureAvailability);
};

switch (availability) {
case RuntimeAvailability::AlwaysAvailable:
// Nothing to do.
break;
case RuntimeAvailability::ConditionallyAvailable: {
isWeakLinked = !isFeatureAvailable();
break;
}
case RuntimeAvailability::AvailableByCompatibilityLibrary: {
if (!isFeatureAvailable())
functionName.append("50");
break;
}
}

llvm::Type *retTy;
if (retTypes.size() == 1)
retTy = *retTypes.begin();
Expand All @@ -612,7 +633,7 @@ llvm::Constant *swift::getRuntimeFn(llvm::Module &Module,
{argTypes.begin(), argTypes.end()},
/*isVararg*/ false);

cache = Module.getOrInsertFunction(name, fnTy);
cache = Module.getOrInsertFunction(functionName.c_str(), fnTy);

// Add any function attributes and set the calling convention.
if (auto fn = dyn_cast<llvm::Function>(cache)) {
Expand Down Expand Up @@ -667,7 +688,7 @@ llvm::Constant *swift::getRuntimeFn(llvm::Module &Module,
llvm::Constant *IRGenModule::get##ID##Fn() { \
using namespace RuntimeConstants; \
return getRuntimeFn(Module, ID##Fn, #NAME, CC, \
(AVAILABILITY)(this->Context), \
AVAILABILITY, &this->Context, \
RETURNS, ARGS, ATTRS); \
}

Expand Down
Loading