Skip to content

Back-deploy creation of global-actor-qualified function type metadata. #39280

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
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
18 changes: 18 additions & 0 deletions include/swift/Runtime/RuntimeFunctions.def
Original file line number Diff line number Diff line change
Expand Up @@ -634,6 +634,24 @@ FUNCTION(GetFunctionMetadataGlobalActor,
TypeMetadataPtrTy),
ATTRS(NoUnwind, ReadOnly))

// Metadata *
// swift_getFunctionTypeMetadataGlobalActorBackDeploy(unsigned long flags,
// unsigned long diffKind,
// const Metadata **parameters,
// const uint32_t *parameterFlags,
// const Metadata *result,
// const Metadata *globalActor);
FUNCTION(GetFunctionMetadataGlobalActorBackDeploy,
swift_getFunctionTypeMetadataGlobalActorBackDeploy,
C_CC, OpaqueTypeAvailability,
RETURNS(TypeMetadataPtrTy),
ARGS(SizeTy,
SizeTy,
TypeMetadataPtrTy->getPointerTo(0),
Int32Ty->getPointerTo(0),
TypeMetadataPtrTy,
TypeMetadataPtrTy),
ATTRS(NoUnwind, ReadOnly))

// Metadata *swift_getFunctionTypeMetadata0(unsigned long flags,
// const Metadata *resultMetadata);
Expand Down
11 changes: 10 additions & 1 deletion lib/IRGen/MetadataRequest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1230,6 +1230,13 @@ static MetadataResponse emitTupleTypeMetadataRef(IRGenFunction &IGF,
}
}

/// Determine whether concurrency support is available in the runtime.
static bool isConcurrencyAvailable(ASTContext &ctx) {
auto deploymentAvailability =
AvailabilityContext::forDeploymentTarget(ctx);
return deploymentAvailability.isContainedIn(ctx.getConcurrencyAvailability());
}

namespace {
/// A visitor class for emitting a reference to a metatype object.
/// This implements a "raw" access, useful for implementing cache
Expand Down Expand Up @@ -1591,7 +1598,9 @@ namespace {
}

auto *getMetadataFn = type->getGlobalActor()
? IGF.IGM.getGetFunctionMetadataGlobalActorFn()
? (isConcurrencyAvailable(IGF.getSwiftModule()->getASTContext())
? IGF.IGM.getGetFunctionMetadataGlobalActorFn()
: IGF.IGM.getGetFunctionMetadataGlobalActorBackDeployFn())
: type->isDifferentiable()
? IGF.IGM.getGetFunctionMetadataDifferentiableFn()
: IGF.IGM.getGetFunctionMetadataFn();
Expand Down
4 changes: 3 additions & 1 deletion stdlib/public/BackDeployConcurrency/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ set(swift_concurrency_install_component back-deployment)
set(swift_concurrency_options
BACK_DEPLOYMENT_LIBRARY 5.5
DARWIN_INSTALL_NAME_DIR "@rpath")
set(swift_concurrency_extra_sources "../BackDeployConcurrency/Exclusivity.cpp")
set(swift_concurrency_extra_sources
"../BackDeployConcurrency/Exclusivity.cpp"
"../BackDeployConcurrency/Metadata.cpp")

add_subdirectory(../Concurrency stdlib/public/BackDeployConcurrency)
255 changes: 255 additions & 0 deletions stdlib/public/BackDeployConcurrency/Metadata.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,255 @@
//===--- Metadata.cpp - Exclusivity tracking ------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// This implements the runtime support for metadata accessors that aren't
// available in Swift runtimes prior to macOS 12/iOS 15 era.
//
//===----------------------------------------------------------------------===//
#include <cinttypes>

#include "swift/Runtime/Concurrent.h"
#include "swift/Runtime/Metadata.h"

using namespace swift;

namespace swift {

SWIFT_EXPORT_FROM(swift_Concurrency)
const FunctionTypeMetadata *
swift_getFunctionTypeMetadataGlobalActorStandalone(
FunctionTypeFlags flags, FunctionMetadataDifferentiabilityKind diffKind,
const Metadata *const *parameters, const uint32_t *parameterFlags,
const Metadata *result, const Metadata *globalActor);

}

namespace {

constexpr size_t roundUpToAlignment(size_t offset, size_t alignment) {
return ((offset + alignment - 1) & ~(alignment - 1));
}

class MetadataAllocator : public llvm::AllocatorBase<MetadataAllocator> {
private:
uint16_t Tag;

public:
constexpr MetadataAllocator(uint16_t tag) : Tag(tag) {}
MetadataAllocator() = delete;

void Reset() {}

LLVM_ATTRIBUTE_RETURNS_NONNULL void *Allocate(size_t size, size_t alignment) {
return malloc(size);
}
using AllocatorBase<MetadataAllocator>::Allocate;

void Deallocate(const void *Ptr, size_t size, size_t Alignment) {
free(const_cast<void *>(Ptr));
}
using AllocatorBase<MetadataAllocator>::Deallocate;

void PrintStats() const {}

MetadataAllocator withTag(uint16_t Tag) {
MetadataAllocator Allocator = *this;
Allocator.Tag = Tag;
return Allocator;
}
};

template <uint16_t StaticTag>
class TaggedMetadataAllocator : public MetadataAllocator {
public:
constexpr TaggedMetadataAllocator() : MetadataAllocator(StaticTag) {}
};

template <class EntryTy, uint16_t Tag>
using SimpleGlobalCache =
StableAddressConcurrentReadableHashMap<EntryTy,
TaggedMetadataAllocator<Tag>>;

class FunctionCacheEntry {
public:
FullMetadata<FunctionTypeMetadata> Data;

struct Key {
const FunctionTypeFlags Flags;
const FunctionMetadataDifferentiabilityKind DifferentiabilityKind;
const Metadata *const *Parameters;
const uint32_t *ParameterFlags;
const Metadata *Result;
const Metadata *GlobalActor;

FunctionTypeFlags getFlags() const { return Flags; }

FunctionMetadataDifferentiabilityKind getDifferentiabilityKind() const {
return DifferentiabilityKind;
}

const Metadata *getParameter(unsigned index) const {
assert(index < Flags.getNumParameters());
return Parameters[index];
}
const Metadata *getResult() const { return Result; }

const uint32_t *getParameterFlags() const {
return ParameterFlags;
}

::ParameterFlags getParameterFlags(unsigned index) const {
assert(index < Flags.getNumParameters());
auto flags = Flags.hasParameterFlags() ? ParameterFlags[index] : 0;
return ParameterFlags::fromIntValue(flags);
}

const Metadata *getGlobalActor() const { return GlobalActor; }

friend llvm::hash_code hash_value(const Key &key) {
auto hash = llvm::hash_combine(
key.Flags.getIntValue(),
key.DifferentiabilityKind.getIntValue(),
key.Result, key.GlobalActor);
for (unsigned i = 0, e = key.getFlags().getNumParameters(); i != e; ++i) {
hash = llvm::hash_combine(hash, key.getParameter(i));
hash = llvm::hash_combine(hash, key.getParameterFlags(i).getIntValue());
}
return hash;
}
};

FunctionCacheEntry(const Key &key);

intptr_t getKeyIntValueForDump() {
return 0; // No single meaningful value here.
}

bool matchesKey(const Key &key) const {
if (key.getFlags().getIntValue() != Data.Flags.getIntValue())
return false;
if (key.getDifferentiabilityKind().Value !=
Data.getDifferentiabilityKind().Value)
return false;
if (key.getResult() != Data.ResultType)
return false;
if (key.getGlobalActor() != Data.getGlobalActor())
return false;
for (unsigned i = 0, e = key.getFlags().getNumParameters(); i != e; ++i) {
if (key.getParameter(i) != Data.getParameter(i))
return false;
if (key.getParameterFlags(i).getIntValue() !=
Data.getParameterFlags(i).getIntValue())
return false;
}
return true;
}

friend llvm::hash_code hash_value(const FunctionCacheEntry &value) {
Key key = {value.Data.Flags, value.Data.getDifferentiabilityKind(),
value.Data.getParameters(), value.Data.getParameterFlags(),
value.Data.ResultType, value.Data.getGlobalActor()};
return hash_value(key);
}

static size_t getExtraAllocationSize(const Key &key) {
return getExtraAllocationSize(key.Flags);
}

size_t getExtraAllocationSize() const {
return getExtraAllocationSize(Data.Flags);
}

static size_t getExtraAllocationSize(const FunctionTypeFlags &flags) {
const auto numParams = flags.getNumParameters();
auto size = numParams * sizeof(FunctionTypeMetadata::Parameter);
if (flags.hasParameterFlags())
size += numParams * sizeof(uint32_t);
if (flags.isDifferentiable())
size = roundUpToAlignment(size, sizeof(void *)) +
sizeof(FunctionMetadataDifferentiabilityKind);
if (flags.hasGlobalActor())
size = roundUpToAlignment(size, sizeof(void *)) + sizeof(Metadata *);
return roundUpToAlignment(size, sizeof(void *));
}
};

} // end anonymous namespace

FunctionCacheEntry::FunctionCacheEntry(const Key &key) {
auto flags = key.getFlags();

// Pick a value witness table appropriate to the function convention.
// All function types of a given convention have the same value semantics,
// so they share a value witness table.
switch (flags.getConvention()) {
case FunctionMetadataConvention::Swift:
if (!flags.isEscaping()) {
Data.ValueWitnesses = &VALUE_WITNESS_SYM(NOESCAPE_FUNCTION_MANGLING);
} else {
Data.ValueWitnesses = &VALUE_WITNESS_SYM(FUNCTION_MANGLING);
}
break;

case FunctionMetadataConvention::Thin:
case FunctionMetadataConvention::CFunctionPointer:
Data.ValueWitnesses = &VALUE_WITNESS_SYM(THIN_FUNCTION_MANGLING);
break;

case FunctionMetadataConvention::Block:
#if SWIFT_OBJC_INTEROP
// Blocks are ObjC objects, so can share the AnyObject value
// witnesses (stored as "BO" rather than "yXl" for ABI compat).
Data.ValueWitnesses = &VALUE_WITNESS_SYM(BO);
#else
assert(false && "objc block without objc interop?");
#endif
break;
}

unsigned numParameters = flags.getNumParameters();

Data.setKind(MetadataKind::Function);
Data.Flags = flags;
Data.ResultType = key.getResult();
if (flags.hasGlobalActor())
*Data.getGlobalActorAddr() = key.getGlobalActor();
if (flags.isDifferentiable())
*Data.getDifferentiabilityKindAddress() = key.getDifferentiabilityKind();

for (unsigned i = 0; i < numParameters; ++i) {
Data.getParameters()[i] = key.getParameter(i);
if (flags.hasParameterFlags())
Data.getParameterFlags()[i] = key.getParameterFlags(i).getIntValue();
}
}

// Provide a definition and static initializer for the fixed seed. This
// initializer should always be zero to ensure its value can never appear to be
// non-zero, even during dynamic initialization.
uint64_t llvm::hashing::detail::fixed_seed_override = 0;

/// The uniquing structure for function type metadata with a global actor.
static SimpleGlobalCache<FunctionCacheEntry, FunctionTypesTag> FunctionTypes;

const FunctionTypeMetadata *
swift::swift_getFunctionTypeMetadataGlobalActorStandalone(
FunctionTypeFlags flags, FunctionMetadataDifferentiabilityKind diffKind,
const Metadata *const *parameters, const uint32_t *parameterFlags,
const Metadata *result, const Metadata *globalActor) {
assert(flags.hasGlobalActor());
assert(flags.hasGlobalActor());
FunctionCacheEntry::Key key = {
flags, diffKind, parameters, parameterFlags, result, globalActor
};
return &FunctionTypes.getOrInsert(key).first->Data;

}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,55 @@
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
#include "swift/Runtime/Metadata.h"
#include <dispatch/dispatch.h>
#include <dlfcn.h>

// Allow this library to get force-loaded by autolinking
__attribute__((weak, visibility("hidden"))) extern "C" char
_swift_FORCE_LOAD_$_swiftCompatibilityConcurrency = 0;
using namespace swift;

namespace swift {

// Entrypoint called by the compiler when back-deploying concurrency, which
// switches between the real implementation of
// swift_getFunctionTypeMetadataGlobalActor and
// swift_getFunctionTypeMetadataGlobalActorStandalone depending on what system
// it is running on.
SWIFT_RUNTIME_STDLIB_INTERNAL
const FunctionTypeMetadata *
swift_getFunctionTypeMetadataGlobalActorBackDeploy(
FunctionTypeFlags flags, FunctionMetadataDifferentiabilityKind diffKind,
const Metadata *const *parameters, const uint32_t *parameterFlags,
const Metadata *result, const Metadata *globalActor);

} // end namespace swift

const FunctionTypeMetadata *
swift::swift_getFunctionTypeMetadataGlobalActorBackDeploy(
FunctionTypeFlags flags, FunctionMetadataDifferentiabilityKind diffKind,
const Metadata *const *parameters, const uint32_t *parameterFlags,
const Metadata *result, const Metadata *globalActor) {
using BuilderFn = const FunctionTypeMetadata *(*)(
FunctionTypeFlags, FunctionMetadataDifferentiabilityKind,
const Metadata *const *, const uint32_t *,
const Metadata *, const Metadata *);
static BuilderFn builderFn;
static dispatch_once_t builderToken;
dispatch_once(&builderToken, ^{
// Prefer the function from the Swift runtime if it is available.
builderFn = reinterpret_cast<BuilderFn>(
dlsym(RTLD_DEFAULT, "swift_getFunctionTypeMetadataGlobalActor"));
if (builderFn)
return;

builderFn = reinterpret_cast<BuilderFn>(
dlsym(RTLD_DEFAULT,
"swift_getFunctionTypeMetadataGlobalActorStandalone"));
});

assert(builderFn && "No way to build global-actor-qualified function type");
return builderFn(
flags, diffKind, parameters, parameterFlags, result, globalActor);
}
2 changes: 1 addition & 1 deletion test/IRGen/global_actor_function_types.sil
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ func globalActorMetatype<T>(_: T.Type) -> Any.Type {
// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc %swift.type* @"$s4test19globalActorMetatypeyypXpxmlF"
// CHECK: [[MAIN_ACTOR_RESPONSE:%[0-9]+]] = call swiftcc %swift.metadata_response @"$sScMMa"(i{{32|64}} 255)
// CHECK-NEXT: [[MAIN_ACTOR_METADATA:%[0-9]+]] = extractvalue %swift.metadata_response [[MAIN_ACTOR_RESPONSE]], 0
// CHECK: call %swift.type* @swift_getFunctionTypeMetadataGlobalActor(i{{32|64}} 335544320, i{{32|64}} 0, %swift.type** null, i32* null, %swift.type* %T, %swift.type* [[MAIN_ACTOR_METADATA]])
// CHECK: call %swift.type* @swift_getFunctionTypeMetadataGlobalActor{{.*}}(i{{32|64}} 335544320, i{{32|64}} 0, %swift.type** null, i32* null, %swift.type* %T, %swift.type* [[MAIN_ACTOR_METADATA]])
sil [ossa] @$s4test19globalActorMetatypeyypXpxmlF : $@convention(thin) <T> (@thick T.Type) -> @thick Any.Type {
bb0(%0 : $@thick T.Type):
%2 = metatype $@thin (@MainActor () -> T).Type
Expand Down
Loading