Skip to content

SIL and IRGen support for @isolated(any) function types #71574

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 1 commit into from
Feb 13, 2024
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
4 changes: 3 additions & 1 deletion docs/ABI/Mangling.rst
Original file line number Diff line number Diff line change
Expand Up @@ -734,14 +734,16 @@ mangled in to disambiguate.
impl-function-type ::= type* 'I' FUNC-ATTRIBUTES '_'
impl-function-type ::= type* generic-signature 'I' FUNC-ATTRIBUTES '_'

FUNC-ATTRIBUTES ::= PATTERN-SUBS? INVOCATION-SUBS? PSEUDO-GENERIC? CALLEE-ESCAPE? DIFFERENTIABILITY-KIND? CALLEE-CONVENTION FUNC-REPRESENTATION? COROUTINE-KIND? SENDABLE? ASYNC? (PARAM-CONVENTION PARAM-DIFFERENTIABILITY?)* RESULT-CONVENTION* ('Y' PARAM-CONVENTION)* ('z' RESULT-CONVENTION RESULT-DIFFERENTIABILITY?)?
FUNC-ATTRIBUTES ::= PATTERN-SUBS? INVOCATION-SUBS? PSEUDO-GENERIC? CALLEE-ESCAPE? ISOLATION? DIFFERENTIABILITY-KIND? CALLEE-CONVENTION FUNC-REPRESENTATION? COROUTINE-KIND? SENDABLE? ASYNC? (PARAM-CONVENTION PARAM-DIFFERENTIABILITY?)* RESULT-CONVENTION* ('Y' PARAM-CONVENTION)* ('z' RESULT-CONVENTION RESULT-DIFFERENTIABILITY?)?

PATTERN-SUBS ::= 's' // has pattern substitutions
INVOCATION-SUB ::= 'I' // has invocation substitutions
PSEUDO-GENERIC ::= 'P'

CALLEE-ESCAPE ::= 'e' // @escaping (inverse of SIL @noescape)

ISOLATION ::= 'A' // @isolated(any)

DIFFERENTIABILITY-KIND ::= 'd' // @differentiable
DIFFERENTIABILITY-KIND ::= 'l' // @differentiable(_linear)
DIFFERENTIABILITY-KIND ::= 'f' // @differentiable(_forward)
Expand Down
51 changes: 44 additions & 7 deletions docs/SIL.rst
Original file line number Diff line number Diff line change
Expand Up @@ -459,6 +459,20 @@ number of ways:
- Other function type conventions are described in ``Properties of Types`` and
``Calling Convention``.

- SIL function types do not directly carry most of the actor-isolation
information available in the Swift type system. Actor isolation is mostly
simply erased from the SIL type system and treated as a dynamic property
in SIL functions.

However, ``@isolated(any)`` requires some additional ABI support and
therefore must be carried on SIL function types. ``@isolated(any)`` is
only allowed in combination with ``@convention(thick)``; in particular,
this precludes SIL function declarations from having ``@isolated(any)``
type. Instead, ``@isolated(any)`` function values are constructed with
``partial_apply [isolated_any]``, which has additional requirements.
The isolation of an ``@isolated(any)`` function can be read with the
``function_extract_isolation`` instruction.

- A SIL function type declares the conventions for its parameters.
The parameters are written as an unlabeled tuple; the elements of that
tuple must be legal SIL types, optionally decorated with one of the
Expand Down Expand Up @@ -6118,12 +6132,13 @@ partial_apply
`````````````
::

sil-instruction ::= 'partial_apply' callee-ownership-attr? on-stack-attr? sil-value
sil-instruction ::= 'partial_apply' partial-apply-attr* sil-value
sil-apply-substitution-list?
'(' (sil-value (',' sil-value)*)? ')'
':' sil-type
callee-ownership-attr ::= '[callee_guaranteed]'
on-stack-attr ::= '[on_stack]'
partial-apply-attr ::= '[callee_guaranteed]'
partial-apply-attr ::= '[isolated_any]'
partial-apply-attr ::= '[on_stack]'

%c = partial_apply %0(%1, %2, ...) : $(Z..., A, B, ...) -> R
// Note that the type of the callee '%0' is specified *after* the arguments
Expand Down Expand Up @@ -6179,13 +6194,24 @@ lowers to an uncurried entry point and is curried in the enclosing function::
return %ret : $Int
}

**Erased Isolation**: If the ``partial_apply`` is marked with the flag
``[isolated_any]``, the first applied argument must have type
``Optional<any Actor>``. In addition to being provided as an argument to
the partially-applied function, this value will be stored in a special
place in the context and can be recovered with ``function_extract_isolation``.
The result type of the ``partial_apply`` will be an ``@isolated(any)``
function type.

**Ownership Semantics of Closure Context during Invocation**: By default, an
escaping ``partial_apply`` (``partial_apply`` without ``[on_stack]]`` creates a
closure whose invocation takes ownership of the context, meaning that a call
implicitly releases the closure. If the ``partial_apply`` is marked with the
flag ``[callee_guaranteed]`` the invocation instead uses a caller-guaranteed
model, where the caller promises not to release the closure while the function
is being called.
implicitly releases the closure.

If the ``partial_apply`` is marked with the flag ``[callee_guaranteed]``,
the invocation instead uses a caller-guaranteed model, where the caller
promises not to release the closure while the function is being called.
The result type of the ``partial_apply`` will be a ``@callee_guaranteed``
function type.

**Captured Value Ownership Semantics**: In the instruction syntax, the type of
the callee is specified after the argument list; the types of the argument and
Expand Down Expand Up @@ -6581,6 +6607,17 @@ autorelease_value

*TODO* Complete this section.

function_extract_isolation
``````````````````````````

::
sil-instruction ::= function_extract_isolation sil-operand

Reads the isolation of a `@isolated(any)` function value. The result is
always a borrowed value of type `$Optional<any Actor>`. It is exactly
the value that was originally used to construct the function with
`partial_apply [isolated_any]`.

tuple
`````
::
Expand Down
19 changes: 19 additions & 0 deletions include/swift/AST/ASTSynthesis.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ enum SingletonTypeSynthesizer {
_word,
_serialExecutor, // the '_Concurrency.SerialExecutor' protocol
_taskExecutor, // the '_Concurrency.TaskExecutor' protocol
_actor, // the '_Concurrency.Actor' protocol
};
inline Type synthesizeType(SynthesisContext &SC,
SingletonTypeSynthesizer kind) {
Expand All @@ -73,6 +74,9 @@ inline Type synthesizeType(SynthesisContext &SC,
case _taskExecutor:
return SC.Context.getProtocol(KnownProtocolKind::TaskExecutor)
->getDeclaredInterfaceType();
case _actor:
return SC.Context.getProtocol(KnownProtocolKind::Actor)
->getDeclaredInterfaceType();
}
}

Expand Down Expand Up @@ -119,6 +123,21 @@ Type synthesizeType(SynthesisContext &SC,
return MetatypeType::get(synthesizeType(SC, M.Sub));
}

/// A synthesizer which generates an existential type from a requirement type.
template <class S>
struct ExistentialTypeSynthesizer {
S Sub;
};
template <class S>
constexpr ExistentialTypeSynthesizer<S> _existential(S sub) {
return {sub};
}
template <class S>
Type synthesizeType(SynthesisContext &SC,
const ExistentialTypeSynthesizer<S> &M) {
return ExistentialType::get(synthesizeType(SC, M.Sub));
}

/// A synthesizer which generates an existential metatype type.
template <class S>
struct ExistentialMetatypeTypeSynthesizer {
Expand Down
7 changes: 7 additions & 0 deletions include/swift/AST/Builtins.def
Original file line number Diff line number Diff line change
Expand Up @@ -1068,6 +1068,13 @@ BUILTIN_MISC_OPERATION_WITH_SILGEN(BuildDefaultActorExecutorRef,
BUILTIN_MISC_OPERATION_WITH_SILGEN(BuildMainActorExecutorRef,
"buildMainActorExecutorRef", "n", Special)

/// extractFunctionIsolation: <T>(_: T) -> (any Actor)?
///
/// Returns the isolation of a value, which must be an @isolated(any)
/// function type.
BUILTIN_MISC_OPERATION_WITH_SILGEN(ExtractFunctionIsolation,
"extractFunctionIsolation", "", Special)

/// getEnumTag: <T>(_: T) -> Builtin.Int32
///
/// Given a dynamic generic value, unsafely assume it is an enum type and call
Expand Down
5 changes: 5 additions & 0 deletions include/swift/AST/DiagnosticsSIL.def
Original file line number Diff line number Diff line change
Expand Up @@ -744,6 +744,11 @@ ERROR(polymorphic_builtin_passed_type_without_static_overload, none, "Static"
"overload implied by passing argument of type %2",
(Identifier, StringRef, Type))

ERROR(builtin_get_function_isolation_bad_argument, none,
"argument to 'extractFunctionIsolation' must have '@isolated(any)' "
"function type",
())

ERROR(box_to_stack_cannot_promote_box_to_stack_due_to_escape_alloc, none,
"Can not promote value from heap to stack due to value escaping", ())
NOTE(box_to_stack_cannot_promote_box_to_stack_due_to_escape_location, none,
Expand Down
3 changes: 3 additions & 0 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -5558,6 +5558,9 @@ ERROR(isolated_any_experimental,none,
ERROR(isolated_attr_global_actor_type,none,
"function type cannot have both a global actor and '@isolated(%0)'",
(StringRef))
ERROR(isolated_attr_bad_convention,none,
"'@isolated(%0)' function type cannot have %1 convention",
(StringRef, StringRef))
ERROR(isolation_macro_experimental,none,
"#isolation macro is experimental", ())

Expand Down
68 changes: 66 additions & 2 deletions include/swift/AST/ExtInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,20 @@ class FunctionTypeIsolation {
}
};

/// For now, the kinds of isolation we carry on SIL function types
/// are significantly reduced compared to AST function types.
/// Isolation is not part of the SIL function model after the
/// early portion of the pipeline.
enum class SILFunctionTypeIsolation {
/// We don't normally record isolation in SIL function types,
/// so the empty case here is "unknown".
Unknown,

/// The isolation of the function has been statically erased.
/// This corresponds to @isolated(any).
Erased,
};

// MARK: - ClangTypeInfo
/// Wrapper class for storing a clang::Type in an (AST|SIL)ExtInfo.
class ClangTypeInfo {
Expand Down Expand Up @@ -893,7 +907,8 @@ class SILExtInfoBuilder {
DifferentiabilityMaskOffset = 9,
DifferentiabilityMask = 0x7 << DifferentiabilityMaskOffset,
UnimplementableMask = 1 << 12,
NumMaskBits = 13
ErasedIsolationMask = 1 << 13,
NumMaskBits = 14
};

unsigned bits; // Naturally sized for speed.
Expand All @@ -913,12 +928,15 @@ class SILExtInfoBuilder {
static constexpr unsigned makeBits(Representation rep, bool isPseudogeneric,
bool isNoEscape, bool isSendable,
bool isAsync, bool isUnimplementable,
SILFunctionTypeIsolation isolation,
DifferentiabilityKind diffKind) {
return ((unsigned)rep) | (isPseudogeneric ? PseudogenericMask : 0) |
(isNoEscape ? NoEscapeMask : 0) |
(isSendable ? SendableMask : 0) |
(isAsync ? AsyncMask : 0) |
(isUnimplementable ? UnimplementableMask : 0) |
(isolation == SILFunctionTypeIsolation::Erased
? ErasedIsolationMask : 0) |
(((unsigned)diffKind << DifferentiabilityMaskOffset) &
DifferentiabilityMask);
}
Expand All @@ -929,22 +947,28 @@ class SILExtInfoBuilder {
SILExtInfoBuilder()
: SILExtInfoBuilder(makeBits(SILFunctionTypeRepresentation::Thick, false,
false, false, false, false,
SILFunctionTypeIsolation::Unknown,
DifferentiabilityKind::NonDifferentiable),
ClangTypeInfo(nullptr), LifetimeDependenceInfo()) {}

SILExtInfoBuilder(Representation rep, bool isPseudogeneric, bool isNoEscape,
bool isSendable, bool isAsync, bool isUnimplementable,
SILFunctionTypeIsolation isolation,
DifferentiabilityKind diffKind, const clang::Type *type,
LifetimeDependenceInfo lifetimeDependenceInfo)
: SILExtInfoBuilder(makeBits(rep, isPseudogeneric, isNoEscape, isSendable,
isAsync, isUnimplementable, diffKind),
isAsync, isUnimplementable,
isolation, diffKind),
ClangTypeInfo(type), lifetimeDependenceInfo) {}

// Constructor for polymorphic type.
SILExtInfoBuilder(ASTExtInfoBuilder info, bool isPseudogeneric)
: SILExtInfoBuilder(makeBits(info.getSILRepresentation(), isPseudogeneric,
info.isNoEscape(), info.isSendable(),
info.isAsync(), /*unimplementable*/ false,
info.getIsolation().isErased()
? SILFunctionTypeIsolation::Erased
: SILFunctionTypeIsolation::Unknown,
info.getDifferentiabilityKind()),
info.getClangTypeInfo(),
info.getLifetimeDependenceInfo()) {}
Expand Down Expand Up @@ -988,6 +1012,18 @@ class SILExtInfoBuilder {
return bits & UnimplementableMask;
}

/// Does this function type have erased isolation (i.e. is it the
/// lowering of an @isolated(any) function type)?
constexpr bool hasErasedIsolation() const {
return bits & ErasedIsolationMask;
}

constexpr SILFunctionTypeIsolation getIsolation() const {
return hasErasedIsolation()
? SILFunctionTypeIsolation::Erased
: SILFunctionTypeIsolation::Unknown;
}

/// Get the underlying ClangTypeInfo value.
ClangTypeInfo getClangTypeInfo() const { return clangTypeInfo; }

Expand Down Expand Up @@ -1071,6 +1107,22 @@ class SILExtInfoBuilder {
clangTypeInfo, lifetimeDependenceInfo);
}
[[nodiscard]]
SILExtInfoBuilder withErasedIsolation(bool erased = true) const {
return SILExtInfoBuilder(erased ? (bits | ErasedIsolationMask)
: (bits & ~ErasedIsolationMask),
clangTypeInfo, lifetimeDependenceInfo);
}
[[nodiscard]]
SILExtInfoBuilder withIsolation(SILFunctionTypeIsolation isolation) const {
switch (isolation) {
case SILFunctionTypeIsolation::Unknown:
return *this;
case SILFunctionTypeIsolation::Erased:
return withErasedIsolation(true);
}
llvm_unreachable("bad kind");
}
[[nodiscard]]
SILExtInfoBuilder withUnimplementable(bool isUnimplementable = true) const {
return SILExtInfoBuilder(isUnimplementable ? (bits | UnimplementableMask)
: (bits & ~UnimplementableMask),
Expand Down Expand Up @@ -1143,6 +1195,7 @@ class SILExtInfo {
static SILExtInfo getThin() {
return SILExtInfoBuilder(SILExtInfoBuilder::Representation::Thin, false,
false, false, false, false,
SILFunctionTypeIsolation::Unknown,
DifferentiabilityKind::NonDifferentiable, nullptr,
LifetimeDependenceInfo())
.build();
Expand Down Expand Up @@ -1175,6 +1228,13 @@ class SILExtInfo {
return builder.isUnimplementable();
}

constexpr bool hasErasedIsolation() const {
return builder.hasErasedIsolation();
}
constexpr SILFunctionTypeIsolation getIsolation() const {
return builder.getIsolation();
}

constexpr DifferentiabilityKind getDifferentiabilityKind() const {
return builder.getDifferentiabilityKind();
}
Expand Down Expand Up @@ -1213,6 +1273,10 @@ class SILExtInfo {
return builder.withAsync(isAsync).build();
}

SILExtInfo withErasedIsolation(bool erased = true) const {
return builder.withErasedIsolation(erased).build();
}

SILExtInfo withUnimplementable(bool isUnimplementable = true) const {
return builder.withUnimplementable(isUnimplementable).build();
}
Expand Down
8 changes: 7 additions & 1 deletion include/swift/AST/Types.h
Original file line number Diff line number Diff line change
Expand Up @@ -384,7 +384,7 @@ class alignas(1 << TypeAlignInBits) TypeBase

protected:
enum { NumAFTExtInfoBits = 14 };
enum { NumSILExtInfoBits = 13 };
enum { NumSILExtInfoBits = 14 };

// clang-format off
union { uint64_t OpaqueBits;
Expand Down Expand Up @@ -4883,6 +4883,10 @@ class SILFunctionType final
bool isSendable() const { return getExtInfo().isSendable(); }
bool isUnimplementable() const { return getExtInfo().isUnimplementable(); }
bool isAsync() const { return getExtInfo().isAsync(); }
bool hasErasedIsolation() const { return getExtInfo().hasErasedIsolation(); }
SILFunctionTypeIsolation getIsolation() const {
return getExtInfo().getIsolation();
}

/// Return the array of all the yields.
ArrayRef<SILYieldInfo> getYields() const {
Expand Down Expand Up @@ -5467,6 +5471,8 @@ class SILFunctionType final
enum innerty {
None,
DifferentFunctionRepresentations,
DifferentAsyncness,
DifferentErasedIsolation,
ABIEscapeToNoEscapeConversion,
DifferentNumberOfResults,
DifferentReturnValueConventions,
Expand Down
1 change: 1 addition & 0 deletions include/swift/Demangling/DemangleNodes.def
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ CONTEXT_NODE(IVarDestroyer)
NODE(ImplEscaping)
NODE(ImplConvention)
NODE(ImplDifferentiabilityKind)
NODE(ImplErasedIsolation)
NODE(ImplParameterResultDifferentiability)
NODE(ImplFunctionAttribute)
NODE(ImplFunctionConvention)
Expand Down
Loading