Skip to content

Commit 58b7bbd

Browse files
Merge pull request #37444 from aschwaighofer/tail_call_opt_force_swift_task_switch_impl
Make sure we tail call optimize a call in concurrency runtime's switch_task_impl.
2 parents f69dfc2 + b0c977f commit 58b7bbd

File tree

2 files changed

+70
-1
lines changed

2 files changed

+70
-1
lines changed

stdlib/public/Concurrency/Actor.cpp

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1800,6 +1800,13 @@ static bool tryAssumeThreadForSwitch(ExecutorRef newExecutor,
18001800
return false;
18011801
}
18021802

1803+
__attribute__((noinline))
1804+
SWIFT_CC(swiftasync)
1805+
static void force_tail_call_hack(AsyncTask *task) {
1806+
// This *should* be executed as a tail call.
1807+
return task->runInFullyEstablishedContext();
1808+
}
1809+
18031810
/// Given that we've assumed control of an executor on this thread,
18041811
/// continue to run the given task on it.
18051812
SWIFT_CC(swiftasync)
@@ -1813,7 +1820,9 @@ static void runOnAssumedThread(AsyncTask *task, ExecutorRef executor,
18131820
oldTracking->setActiveExecutor(executor);
18141821

18151822
// FIXME: force tail call
1816-
return task->runInFullyEstablishedContext();
1823+
// return task->runInFullyEstablishedContext();
1824+
// This hack "ensures" that this call gets executed as a tail call.
1825+
return force_tail_call_hack(task);
18171826
}
18181827

18191828
// Otherwise, set up tracking info.

test/Interpreter/async_fib.swift

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency %s -parse-as-library -module-name main -o %t/main
3+
// RUN: %target-codesign %t/main
4+
// RUN: %target-run %t/main | %FileCheck %s
5+
6+
// REQUIRES: concurrency
7+
// REQUIRES: executable_test
8+
// UNSUPPORTED: use_os_stdlib
9+
// UNSUPPORTED: back_deployment_runtime
10+
// UNSUPPORTED: OS=windows-msvc
11+
12+
var gg = 0
13+
14+
@inline(never)
15+
public func identity<T>(_ x: T) -> T {
16+
gg += 1
17+
return x
18+
}
19+
20+
actor Actor {
21+
var x: Int = 0
22+
init(x: Int) { self.x = x }
23+
24+
@inline(never)
25+
func get(_ i: Int ) async -> Int {
26+
return i + x
27+
}
28+
}
29+
30+
// Used to crash with an stack overflow with m >= 18
31+
let m = 22
32+
33+
@inline(never)
34+
func asyncFib(_ n: Int, _ a1: Actor, _ a2: Actor) async -> Int {
35+
if n == 0 {
36+
return await a1.get(n)
37+
}
38+
if n == 1 {
39+
return await a2.get(n)
40+
}
41+
42+
let first = await asyncFib(n-2, a1, a2)
43+
let second = await asyncFib(n-1, a1, a2)
44+
45+
let result = first + second
46+
47+
return result
48+
}
49+
50+
@main struct Main {
51+
static func main() async {
52+
let a1 = Actor(x: 0)
53+
let a2 = Actor(x: 0)
54+
_ = await asyncFib(identity(m), a1, a2)
55+
56+
// CHECK: result: 0
57+
await print("result: \(a1.x)");
58+
await print(a2.x)
59+
}
60+
}

0 commit comments

Comments
 (0)