Skip to content
This repository was archived by the owner on Jan 10, 2023. It is now read-only.

Commit 15aedcf

Browse files
committed
Merge branch 'main' of github.com:apple/swift into tensorflow-stage
* 'main' of github.com:apple/swift: Make Float16 available for macOS on Apple Silicon (swiftlang#34821) [concurrency] Correctly handle dead-end and unreachable blocks in OptimizeHopToExecutor [Concurrency] Remove the C++ runtime test for swift_future_task_wait. [Concurrency] Implement Task.Handle.get() in terms of an async runtime call. SILGen: Update emitForeignToNativeThunk to handle async methods.
2 parents 3e30dab + 6722d71 commit 15aedcf

File tree

19 files changed

+349
-327
lines changed

19 files changed

+349
-327
lines changed

include/swift/Runtime/Concurrency.h

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -110,31 +110,31 @@ swift_task_escalate(AsyncTask *task, JobPriority newPriority);
110110

111111
/// The result of waiting for a task future.
112112
struct TaskFutureWaitResult {
113-
enum Kind : uintptr_t {
114-
/// The waiting task has been added to the future's wait queue, and will
115-
/// be scheduled once the future has completed.
116-
Waiting,
117-
118-
/// The future succeeded and produced a result value. \c storage points
119-
/// at that value.
120-
Success,
121-
122-
/// The future finished by throwing an error. \c storage is that error
123-
/// existential.
124-
Error,
125-
};
126-
127-
Kind kind;
113+
/// Whether the storage represents the error result vs. the successful
114+
/// result.
115+
bool hadErrorResult;
116+
117+
/// Storage for the result of the future.
118+
///
119+
/// When the future completed normally, this is a pointer to the storage
120+
/// of the result value, which lives inside the future task itself.
121+
///
122+
/// When the future completed by throwing an error, this is the error
123+
/// object itself.
128124
OpaqueValue *storage;
129125
};
130126

131127
/// Wait for a future task to complete.
132128
///
133-
/// This can be called from any thread.
129+
/// This can be called from any thread. Its Swift signature is
134130
///
131+
/// \code
132+
/// func swift_task_future_wait(on task: Builtin.NativeObject) async
133+
/// -> TaskFutureWaitResult
134+
/// \endcode
135135
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
136-
TaskFutureWaitResult
137-
swift_task_future_wait(AsyncTask *task, AsyncTask *waitingTask);
136+
AsyncFunctionType<TaskFutureWaitResult(AsyncTask *task)>
137+
swift_task_future_wait;
138138

139139
/// Add a status record to a task. The record should not be
140140
/// modified while it is registered with a task.

lib/SILGen/SILGenBridging.cpp

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1662,11 +1662,19 @@ void SILGenFunction::emitForeignToNativeThunk(SILDeclRef thunk) {
16621662
getConstantInfo(getTypeExpansionContext(), foreignDeclRef);
16631663
auto foreignFnTy = foreignCI.SILFnType;
16641664

1665-
// Find the foreign error convention and 'self' parameter index.
1665+
// Find the foreign error/async convention and 'self' parameter index.
1666+
bool hasError = false;
1667+
Optional<ForeignAsyncConvention> foreignAsync;
1668+
if (nativeFnTy->isAsync()) {
1669+
foreignAsync = fd->getForeignAsyncConvention();
1670+
assert(foreignAsync && "couldn't find foreign async convention?!");
1671+
}
16661672
Optional<ForeignErrorConvention> foreignError;
16671673
if (nativeFnTy->hasErrorResult()) {
1674+
hasError = true;
16681675
foreignError = fd->getForeignErrorConvention();
1669-
assert(foreignError && "couldn't find foreign error convention!");
1676+
assert((foreignError || foreignAsync)
1677+
&& "couldn't find foreign error or async convention for foreign error!");
16701678
}
16711679
ImportAsMemberStatus memberStatus = fd->getImportAsMemberStatus();
16721680

@@ -1707,7 +1715,7 @@ void SILGenFunction::emitForeignToNativeThunk(SILDeclRef thunk) {
17071715

17081716
// Set up the throw destination if necessary.
17091717
CleanupLocation cleanupLoc(fd);
1710-
if (foreignError) {
1718+
if (hasError) {
17111719
prepareRethrowEpilog(cleanupLoc);
17121720
}
17131721

@@ -1719,14 +1727,20 @@ void SILGenFunction::emitForeignToNativeThunk(SILDeclRef thunk) {
17191727
SmallVector<ManagedValue, 8> args;
17201728
unsigned foreignArgIndex = 0;
17211729

1722-
// A helper function to add a function error argument in the
1730+
// A helper function to add a placeholder for a foreign argument in the
17231731
// appropriate position.
1724-
auto maybeAddForeignErrorArg = [&] {
1725-
if (foreignError &&
1726-
foreignArgIndex == foreignError->getErrorParameterIndex()) {
1732+
auto maybeAddForeignArg = [&]() -> bool {
1733+
if ((foreignError
1734+
&& foreignArgIndex == foreignError->getErrorParameterIndex())
1735+
|| (foreignAsync
1736+
&& foreignArgIndex == foreignAsync->completionHandlerParamIndex()))
1737+
{
17271738
args.push_back(ManagedValue());
17281739
++foreignArgIndex;
1740+
return true;
17291741
}
1742+
1743+
return false;
17301744
};
17311745

17321746
{
@@ -1768,7 +1782,7 @@ void SILGenFunction::emitForeignToNativeThunk(SILDeclRef thunk) {
17681782
llvm_unreachable("unsupported convention");
17691783
}
17701784

1771-
maybeAddForeignErrorArg();
1785+
while (maybeAddForeignArg());
17721786

17731787
bool isSelf = (hasSelfParam && nativeParamIndex == params.size() - 1);
17741788

@@ -1825,7 +1839,7 @@ void SILGenFunction::emitForeignToNativeThunk(SILDeclRef thunk) {
18251839
}
18261840
}
18271841

1828-
maybeAddForeignErrorArg();
1842+
while (maybeAddForeignArg());
18291843

18301844
// Call the original.
18311845
auto subs = getForwardingSubstitutionMap();
@@ -1846,7 +1860,7 @@ void SILGenFunction::emitForeignToNativeThunk(SILDeclRef thunk) {
18461860
bridgedFormalResultType),
18471861
nativeFormalResultType,
18481862
foreignError,
1849-
{}, // TODO foreignAsync
1863+
foreignAsync,
18501864
ImportAsMemberStatus());
18511865

18521866
auto init = indirectResult

lib/SILOptimizer/Mandatory/OptimizeHopToExecutor.cpp

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -144,40 +144,44 @@ void OptimizeHopToExecutor::allocateBlockStates() {
144144
/// Solve the dataflow in forward direction.
145145
void OptimizeHopToExecutor::solveDataflowForward() {
146146
bool changed = false;
147+
bool firstRound = true;
147148
do {
148149
changed = false;
149150
for (BlockState &state : blockStates) {
150151
int newEntry = state.entry;
151152
for (SILBasicBlock *pred : state.block->getPredecessorBlocks()) {
152153
newEntry = BlockState::merge(newEntry, block2State[pred]->exit);
153154
}
154-
if (newEntry != state.entry || state.exit == BlockState::NotSet) {
155+
if (newEntry != state.entry || firstRound) {
155156
changed = true;
156157
state.entry = newEntry;
157158
if (state.intra == BlockState::NotSet)
158159
state.exit = state.entry;
159160
}
160161
}
162+
firstRound = false;
161163
} while (changed);
162164
}
163165

164166
/// Solve the dataflow in backward direction.
165167
void OptimizeHopToExecutor::solveDataflowBackward() {
166168
bool changed = false;
169+
bool firstRound = true;
167170
do {
168171
changed = false;
169172
for (BlockState &state : llvm::reverse(blockStates)) {
170173
int newExit = state.exit;
171174
for (SILBasicBlock *succ : state.block->getSuccessorBlocks()) {
172175
newExit = BlockState::merge(newExit, block2State[succ]->entry);
173176
}
174-
if (newExit != state.exit || state.entry == BlockState::NotSet) {
177+
if (newExit != state.exit || firstRound) {
175178
changed = true;
176179
state.exit = newExit;
177180
if (state.intra == BlockState::NotSet)
178181
state.entry = state.exit;
179182
}
180183
}
184+
firstRound = false;
181185
} while (changed);
182186
}
183187

@@ -255,7 +259,7 @@ bool OptimizeHopToExecutor::removeDeadHopToExecutors() {
255259
// might require a dedicated executor, don't remove a preceeding
256260
// hop_to_executor instruction.
257261
for (BlockState &state : blockStates) {
258-
state.exit = (state.block->getTerminator()->isFunctionExiting() ?
262+
state.exit = (state.block->getSuccessors().empty() ?
259263
BlockState::NoExecutorNeeded : BlockState::NotSet);
260264
state.intra = BlockState::NotSet;
261265
for (SILInstruction &inst : llvm::reverse(*state.block)) {

stdlib/public/Concurrency/Task.cpp

Lines changed: 78 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,56 @@ FutureFragment::Status AsyncTask::waitFuture(AsyncTask *waitingTask) {
7373
}
7474
}
7575

76+
77+
namespace {
78+
79+
/// An asynchronous context within a task that describes a general "Future".
80+
/// task.
81+
///
82+
/// This type matches the ABI of a function `<T> () async throws -> T`, which
83+
/// is the type used by `Task.runDetached` and `Task.group.add` to create
84+
/// futures.
85+
class TaskFutureWaitAsyncContext : public AsyncContext {
86+
public:
87+
// Error result is always present.
88+
SwiftError *errorResult = nullptr;
89+
90+
// No indirect results.
91+
92+
TaskFutureWaitResult result;
93+
94+
// FIXME: Currently, this is always here, but it isn't technically
95+
// necessary.
96+
void* Self;
97+
98+
// Arguments.
99+
AsyncTask *task;
100+
101+
using AsyncContext::AsyncContext;
102+
};
103+
104+
}
105+
106+
/// Run the given task, privoding it with the result of the future.
107+
static void runTaskWithFutureResult(
108+
AsyncTask *waitingTask, ExecutorRef executor,
109+
FutureFragment *futureFragment, bool hadErrorResult) {
110+
auto waitingTaskContext =
111+
static_cast<TaskFutureWaitAsyncContext *>(waitingTask->ResumeContext);
112+
113+
waitingTaskContext->result.hadErrorResult = hadErrorResult;
114+
if (hadErrorResult) {
115+
waitingTaskContext->result.storage =
116+
reinterpret_cast<OpaqueValue *>(futureFragment->getError());
117+
} else {
118+
waitingTaskContext->result.storage = futureFragment->getStoragePtr();
119+
}
120+
121+
// TODO: schedule this task on the executor rather than running it
122+
// directly.
123+
waitingTask->run(executor);
124+
}
125+
76126
void AsyncTask::completeFuture(AsyncContext *context, ExecutorRef executor) {
77127
using Status = FutureFragment::Status;
78128
using WaitQueueItem = FutureFragment::WaitQueueItem;
@@ -103,9 +153,8 @@ void AsyncTask::completeFuture(AsyncContext *context, ExecutorRef executor) {
103153
// Find the next waiting task.
104154
auto nextWaitingTask = waitingTask->getNextWaitingTask();
105155

106-
// TODO: schedule this task on the executor rather than running it
107-
// directly.
108-
waitingTask->run(executor);
156+
// Run the task.
157+
runTaskWithFutureResult(waitingTask, executor, fragment, hadErrorResult);
109158

110159
// Move to the next task.
111160
waitingTask = nextWaitingTask;
@@ -264,21 +313,37 @@ AsyncTaskAndContext swift::swift_task_create_future_f(
264313
return {task, initialContext};
265314
}
266315

267-
TaskFutureWaitResult
268-
swift::swift_task_future_wait(AsyncTask *task, AsyncTask *waitingTask) {
316+
void swift::swift_task_future_wait(
317+
AsyncTask *waitingTask, ExecutorRef executor,
318+
AsyncContext *rawContext) {
319+
// Suspend the waiting task.
320+
waitingTask->ResumeTask = rawContext->ResumeParent;
321+
waitingTask->ResumeContext = rawContext;
322+
323+
// Wait on the future.
324+
auto context = static_cast<TaskFutureWaitAsyncContext *>(rawContext);
325+
auto task = context->task;
269326
assert(task->isFuture());
270327
switch (task->waitFuture(waitingTask)) {
271328
case FutureFragment::Status::Executing:
272-
return TaskFutureWaitResult{TaskFutureWaitResult::Waiting, nullptr};
329+
// The waiting task has been queued on the future.
330+
return;
273331

274332
case FutureFragment::Status::Success:
275-
return TaskFutureWaitResult{
276-
TaskFutureWaitResult::Success, task->futureFragment()->getStoragePtr()};
277-
278-
case FutureFragment::Status::Error:
279-
return TaskFutureWaitResult{
280-
TaskFutureWaitResult::Error,
281-
reinterpret_cast<OpaqueValue *>(task->futureFragment()->getError())};
333+
// Run the task with a successful result.
334+
// FIXME: Want to guarantee a tail call here
335+
runTaskWithFutureResult(
336+
waitingTask, executor, task->futureFragment(),
337+
/*hadErrorResult=*/false);
338+
return;
339+
340+
case FutureFragment::Status::Error:
341+
// Run the task with an error result.
342+
// FIXME: Want to guarantee a tail call here
343+
runTaskWithFutureResult(
344+
waitingTask, executor, task->futureFragment(),
345+
/*hadErrorResult=*/true);
346+
return;
282347
}
283348
}
284349

0 commit comments

Comments
 (0)