Skip to content
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
83 changes: 45 additions & 38 deletions lib/IRGen/GenCall.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4470,44 +4470,9 @@ void irgen::emitTaskCancel(IRGenFunction &IGF, llvm::Value *task) {
call->setCallingConv(IGF.IGM.SwiftCC);
}

llvm::Value *irgen::emitTaskCreate(IRGenFunction &IGF, llvm::Value *flags,
llvm::Value *taskGroup,
llvm::Value *futureResultType,
llvm::Value *taskFunction,
llvm::Value *localContextInfo,
SubstitutionMap subs) {
// Start with empty task options.
llvm::Value *taskOptions =
llvm::ConstantInt::get(IGF.IGM.SwiftTaskOptionRecordPtrTy, 0);

// If there is a task group, emit a task group option structure to contain it.
if (taskGroup) {
TaskOptionRecordFlags optionsFlags(TaskOptionRecordKind::TaskGroup);
llvm::Value *optionsFlagsVal = llvm::ConstantInt::get(
IGF.IGM.SizeTy, optionsFlags.getOpaqueValue());

auto optionsRecord = IGF.createAlloca(
IGF.IGM.SwiftTaskGroupTaskOptionRecordTy, Alignment(),
"task_group_options");
auto optionsBaseRecord = IGF.Builder.CreateStructGEP(
optionsRecord, 0, Size());

// Flags
IGF.Builder.CreateStore(
optionsFlagsVal,
IGF.Builder.CreateStructGEP(optionsBaseRecord, 0, Size()));
// Parent
IGF.Builder.CreateStore(
taskOptions, IGF.Builder.CreateStructGEP(optionsBaseRecord, 1, Size()));
// TaskGroup
IGF.Builder.CreateStore(
taskGroup, IGF.Builder.CreateStructGEP(optionsRecord, 1, Size()));

taskOptions = IGF.Builder.CreateBitOrPointerCast(
optionsRecord.getAddress(), IGF.IGM.SwiftTaskOptionRecordPtrTy);
}

// In embedded Swift, create and pass result type info.
llvm::Value *irgen::addEmbeddedSwiftResultTypeInfo(IRGenFunction &IGF,
llvm::Value *taskOptions,
SubstitutionMap subs) {
if (IGF.IGM.Context.LangOpts.hasFeature(Feature::Embedded)) {
auto optionsRecord =
IGF.createAlloca(IGF.IGM.SwiftResultTypeInfoTaskOptionRecordTy,
Expand Down Expand Up @@ -4561,6 +4526,48 @@ llvm::Value *irgen::emitTaskCreate(IRGenFunction &IGF, llvm::Value *flags,
taskOptions = IGF.Builder.CreateBitOrPointerCast(
optionsRecord.getAddress(), IGF.IGM.SwiftTaskOptionRecordPtrTy);
}
return taskOptions;
}

llvm::Value *irgen::emitTaskCreate(IRGenFunction &IGF, llvm::Value *flags,
llvm::Value *taskGroup,
llvm::Value *futureResultType,
llvm::Value *taskFunction,
llvm::Value *localContextInfo,
SubstitutionMap subs) {
// Start with empty task options.
llvm::Value *taskOptions =
llvm::ConstantInt::get(IGF.IGM.SwiftTaskOptionRecordPtrTy, 0);

// If there is a task group, emit a task group option structure to contain it.
if (taskGroup) {
TaskOptionRecordFlags optionsFlags(TaskOptionRecordKind::TaskGroup);
llvm::Value *optionsFlagsVal = llvm::ConstantInt::get(
IGF.IGM.SizeTy, optionsFlags.getOpaqueValue());

auto optionsRecord = IGF.createAlloca(
IGF.IGM.SwiftTaskGroupTaskOptionRecordTy, Alignment(),
"task_group_options");
auto optionsBaseRecord = IGF.Builder.CreateStructGEP(
optionsRecord, 0, Size());

// Flags
IGF.Builder.CreateStore(
optionsFlagsVal,
IGF.Builder.CreateStructGEP(optionsBaseRecord, 0, Size()));
// Parent
IGF.Builder.CreateStore(
taskOptions, IGF.Builder.CreateStructGEP(optionsBaseRecord, 1, Size()));
// TaskGroup
IGF.Builder.CreateStore(
taskGroup, IGF.Builder.CreateStructGEP(optionsRecord, 1, Size()));

taskOptions = IGF.Builder.CreateBitOrPointerCast(
optionsRecord.getAddress(), IGF.IGM.SwiftTaskOptionRecordPtrTy);
}

// In embedded Swift, create and pass result type info.
taskOptions = addEmbeddedSwiftResultTypeInfo(IGF, taskOptions, subs);

assert(futureResultType && "no future?!");
llvm::CallInst *result = IGF.Builder.CreateCall(
Expand Down
4 changes: 4 additions & 0 deletions lib/IRGen/GenCall.h
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,10 @@ namespace irgen {

void emitTaskCancel(IRGenFunction &IGF, llvm::Value *task);

llvm::Value *addEmbeddedSwiftResultTypeInfo(IRGenFunction &IGF,
llvm::Value *taskOptions,
SubstitutionMap subs);

/// Emit a call to swift_task_create[_f] with the given flags, options, and
/// task function.
llvm::Value *emitTaskCreate(
Expand Down
12 changes: 11 additions & 1 deletion lib/IRGen/GenConcurrency.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

#include "BitPatternBuilder.h"
#include "ExtraInhabitants.h"
#include "GenCall.h"
#include "GenProto.h"
#include "GenType.h"
#include "IRGenDebugInfo.h"
Expand Down Expand Up @@ -226,7 +227,13 @@ llvm::Value *irgen::emitBuiltinStartAsyncLet(IRGenFunction &IGF,
assert(subs.getReplacementTypes().size() == 1 &&
"startAsyncLet should have a type substitution");
auto futureResultType = subs.getReplacementTypes()[0]->getCanonicalType();
auto futureResultTypeMetadata = IGF.emitAbstractTypeMetadataRef(futureResultType);

llvm::Value *futureResultTypeMetadata =
llvm::ConstantPointerNull::get(IGF.IGM.Int8PtrTy);
if (!IGF.IGM.Context.LangOpts.hasFeature(Feature::Embedded)) {
futureResultTypeMetadata =
IGF.emitAbstractTypeMetadataRef(futureResultType);
}

// The concurrency runtime for older Apple OSes has a bug in task formation
// for `async let`s that may manifest when trying to use room in the
Expand Down Expand Up @@ -269,6 +276,9 @@ llvm::Value *irgen::emitBuiltinStartAsyncLet(IRGenFunction &IGF,
IGF.IGM.markAsyncFunctionPointerForPadding(taskAsyncFunctionPointer);
}
}

// In embedded Swift, create and pass result type info.
taskOptions = addEmbeddedSwiftResultTypeInfo(IGF, taskOptions, subs);

llvm::CallInst *call;
if (localResultBuffer) {
Expand Down
4 changes: 4 additions & 0 deletions stdlib/public/Concurrency/AsyncLet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -393,7 +393,11 @@ static void _asyncLet_finish_continuation(

// Destroy the error, or the result that was stored to the buffer.
if (error) {
#if SWIFT_CONCURRENCY_EMBEDDED
swift_unreachable("untyped error used in embedded Swift");
#else
swift_errorRelease((SwiftError*)error);
#endif
} else {
alet->getTask()->futureFragment()->getResultType().vw_destroy(resultBuffer);
}
Expand Down
28 changes: 25 additions & 3 deletions stdlib/public/Concurrency/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@ if(SWIFT_SHOULD_BUILD_EMBEDDED_STDLIB AND SWIFT_SHOULD_BUILD_EMBEDDED_CONCURRENC
set(SWIFT_STDLIB_STABLE_ABI OFF)
set(SWIFT_STDLIB_ENABLE_OBJC_INTEROP OFF)
set(SWIFT_STDLIB_SINGLE_THREADED_CONCURRENCY TRUE)
set(SWIFT_STDLIB_CONCURRENCY_TRACING FALSE)

foreach(entry ${EMBEDDED_STDLIB_TARGET_TRIPLES})
string(REGEX REPLACE "[ \t]+" ";" list "${entry}")
Expand All @@ -210,12 +211,33 @@ if(SWIFT_SHOULD_BUILD_EMBEDDED_STDLIB AND SWIFT_SHOULD_BUILD_EMBEDDED_CONCURRENC
IS_STDLIB

${SWIFT_RUNTIME_CONCURRENCY_C_SOURCES}
# TODO: Only a handful of Swift Concurrency .swift sources, for now.
Task.swift
# TODO: Only a subset of Swift Concurrency .swift sources, for now.
Actor.swift
AsyncLet.swift
Executor.swift
CheckedContinuation.swift
Errors.swift
Executor.swift
ExecutorAssertions.swift
AsyncCompactMapSequence.swift
AsyncDropFirstSequence.swift
AsyncDropWhileSequence.swift
AsyncFilterSequence.swift
AsyncFlatMapSequence.swift
AsyncIteratorProtocol.swift
AsyncMapSequence.swift
AsyncPrefixSequence.swift
AsyncPrefixWhileSequence.swift
AsyncSequence.swift
AsyncThrowingCompactMapSequence.swift
AsyncThrowingDropWhileSequence.swift
AsyncThrowingFilterSequence.swift
AsyncThrowingFlatMapSequence.swift
AsyncThrowingMapSequence.swift
AsyncThrowingPrefixWhileSequence.swift
GlobalActor.swift
PartialAsyncTask.swift
Task.swift
TaskCancellation.swift

SWIFT_COMPILE_FLAGS
-Xcc -D__MACH__ -Xcc -D__APPLE__ -Xcc -ffreestanding -enable-experimental-feature Embedded
Expand Down
5 changes: 5 additions & 0 deletions stdlib/public/Concurrency/CheckedContinuation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ internal func logFailedCheck(_ message: UnsafeRawPointer)
/// Implementation class that holds the `UnsafeContinuation` instance for
/// a `CheckedContinuation`.
@available(SwiftStdlib 5.1, *)
@_unavailableInEmbedded
internal final class CheckedContinuationCanary: @unchecked Sendable {
// The instance state is stored in tail-allocated raw memory, so that
// we can atomically check the continuation state.
Expand Down Expand Up @@ -119,6 +120,7 @@ internal final class CheckedContinuationCanary: @unchecked Sendable {
/// you can replace one with the other in most circumstances,
/// without making other changes.
@available(SwiftStdlib 5.1, *)
@_unavailableInEmbedded
public struct CheckedContinuation<T, E: Error>: Sendable {
private let canary: CheckedContinuationCanary

Expand Down Expand Up @@ -187,6 +189,7 @@ public struct CheckedContinuation<T, E: Error>: Sendable {
}

@available(SwiftStdlib 5.1, *)
@_unavailableInEmbedded
extension CheckedContinuation {
/// Resume the task awaiting the continuation by having it either
/// return normally or throw an error based on the state of the given
Expand Down Expand Up @@ -281,6 +284,7 @@ extension CheckedContinuation {
@available(SwiftStdlib 5.1, *)
@_unsafeInheritExecutor // ABI compatibility with Swift 5.1
@inlinable
@_unavailableInEmbedded
public func withCheckedContinuation<T>(
function: String = #function,
_ body: (CheckedContinuation<T, Never>) -> Void
Expand Down Expand Up @@ -321,6 +325,7 @@ public func withCheckedContinuation<T>(
@available(SwiftStdlib 5.1, *)
@_unsafeInheritExecutor // ABI compatibility with Swift 5.1
@inlinable
@_unavailableInEmbedded
public func withCheckedThrowingContinuation<T>(
function: String = #function,
_ body: (CheckedContinuation<T, Error>) -> Void
Expand Down
7 changes: 7 additions & 0 deletions stdlib/public/Concurrency/ExecutorAssertions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ extension SerialExecutor {
/// never called. Failure to satisfy that assumption is a serious
/// programming error.
@available(SwiftStdlib 5.9, *)
@_unavailableInEmbedded
public func preconditionIsolated(
_ message: @autoclosure () -> String = String(),
file: StaticString = #fileID, line: UInt = #line
Expand Down Expand Up @@ -72,6 +73,7 @@ extension Actor {
/// never called. Failure to satisfy that assumption is a serious
/// programming error.
@available(SwiftStdlib 5.9, *)
@_unavailableInEmbedded
public nonisolated func preconditionIsolated(
_ message: @autoclosure () -> String = String(),
file: StaticString = #fileID, line: UInt = #line
Expand Down Expand Up @@ -108,6 +110,7 @@ extension GlobalActor {
/// never called. Failure to satisfy that assumption is a serious
/// programming error.
@available(SwiftStdlib 5.9, *)
@_unavailableInEmbedded
public static func preconditionIsolated(
_ message: @autoclosure () -> String = String(),
file: StaticString = #fileID, line: UInt = #line
Expand All @@ -134,6 +137,7 @@ extension SerialExecutor {
/// may assume that it *always* evaluates to `true`. Failure to satisfy that
/// assumption is a serious programming error.
@available(SwiftStdlib 5.9, *)
@_unavailableInEmbedded
public func assertIsolated(
_ message: @autoclosure () -> String = String(),
file: StaticString = #fileID, line: UInt = #line
Expand Down Expand Up @@ -167,6 +171,7 @@ extension Actor {
/// may assume that it *always* evaluates to `true`. Failure to satisfy that
/// assumption is a serious programming error.
@available(SwiftStdlib 5.9, *)
@_unavailableInEmbedded
public nonisolated func assertIsolated(
_ message: @autoclosure () -> String = String(),
file: StaticString = #fileID, line: UInt = #line
Expand Down Expand Up @@ -201,6 +206,7 @@ extension GlobalActor {
/// may assume that it *always* evaluates to `true`. Failure to satisfy that
/// assumption is a serious programming error.
@available(SwiftStdlib 5.9, *)
@_unavailableInEmbedded
public static func assertIsolated(
_ message: @autoclosure () -> String = String(),
file: StaticString = #fileID, line: UInt = #line
Expand Down Expand Up @@ -229,6 +235,7 @@ extension Actor {
/// perspective, the serial executor guarantees mutual exclusion of those two actors.
@available(SwiftStdlib 5.9, *)
@_unavailableFromAsync(message: "express the closure as an explicit function declared on the specified 'actor' instead")
@_unavailableInEmbedded
public nonisolated func assumeIsolated<T>(
_ operation: (isolated Self) throws -> T,
file: StaticString = #fileID, line: UInt = #line
Expand Down
4 changes: 4 additions & 0 deletions stdlib/public/Concurrency/Task.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1236,11 +1236,15 @@ void swift_task_future_wait_throwingImpl(
}

case FutureFragment::Status::Error: {
#if SWIFT_CONCURRENCY_EMBEDDED
swift_unreachable("untyped error used in embedded Swift");
#else
// Run the task with an error result.
auto future = task->futureFragment();
auto error = future->getError();
swift_errorRetain(error);
return resumeFunction(callerContext, error);
#endif
}
}
}
Expand Down
1 change: 0 additions & 1 deletion stdlib/public/Concurrency/Task.swift
Original file line number Diff line number Diff line change
Expand Up @@ -840,7 +840,6 @@ extension Task where Failure == Error {
// ==== Voluntary Suspension -----------------------------------------------------

@available(SwiftStdlib 5.1, *)
@_unavailableInEmbedded
extension Task where Success == Never, Failure == Never {

/// Suspends the current task and allows other tasks to execute.
Expand Down
1 change: 1 addition & 0 deletions stdlib/public/Concurrency/TaskCancellation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ extension Task where Success == Never, Failure == Never {
/// The error is always an instance of `CancellationError`.
///
/// - SeeAlso: `isCancelled()`
@_unavailableInEmbedded
public static func checkCancellation() throws {
if Task<Never, Never>.isCancelled {
throw _Concurrency.CancellationError()
Expand Down
73 changes: 73 additions & 0 deletions test/embedded/concurrency-actors.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// RUN: %empty-directory(%t)
// RUN: %target-swift-frontend -target %target-cpu-apple-macos14 -enable-experimental-feature Embedded -parse-as-library %s %S/Inputs/print.swift -c -o %t/a.o
// RUN: %target-clang -x c -c %S/Inputs/print.c -o %t/print.o
// RUN: %target-clang %t/a.o %t/print.o -o %t/a.out %swift_obj_root/lib/swift/embedded/%target-cpu-apple-macos/libswift_Concurrency.a -dead_strip
// RUN: %target-run %t/a.out | %FileCheck %s

// REQUIRES: swift_in_compiler
// REQUIRES: optimized_stdlib
// REQUIRES: VENDOR=apple
// REQUIRES: OS=macosx

import _Concurrency

actor Number {
var val: Int
var task: Task<Void, Never>?

func increment() {
print("did increment")
val += 1
}

func fib(n: Int) -> Int {
if n < 2 {
return n
}
return fib(n: n-1) + fib(n: n-2)
}

init() async {
val = 0

task = Task.detached(priority: .high) { await self.increment() }

// do some synchronous work
let ans = fib(n: 37)
guard ans == 24157817 else {
fatalError("miscomputation?")
}

// make sure task didn't modify me!
guard val == 0 else {
fatalError("race!")
}

print("finished init()")
}

init(iterations: Int) async {
var iter = iterations
repeat {
val = iter
iter -= 1
} while iter > 0
}
}

@main struct Main {
static func main() async {

// CHECK: finished init()
// CHECK-NEXT: did increment

let n1 = await Number()
await n1.task!.value

let n2 = await Number(iterations: 1000)
let val = await n2.val
guard val == 1 else {
fatalError("wrong val setting")
}
}
}
Loading