Skip to content

Commit 05c6c90

Browse files
authored
Merge pull request #58474 from kavon/undo-fix-cont-jump-to-null
Back out "Fix jump-to-null issue for ObjC async calls."
2 parents c891f34 + 159db26 commit 05c6c90

File tree

8 files changed

+126
-30
lines changed

8 files changed

+126
-30
lines changed

lib/SIL/IR/SILInstruction.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1646,6 +1646,10 @@ bool SILInstruction::maySuspend() const {
16461646
// await_async_continuation always suspends the current task.
16471647
if (isa<AwaitAsyncContinuationInst>(this))
16481648
return true;
1649+
1650+
// hop_to_executor also may cause a suspension
1651+
if (isa<HopToExecutorInst>(this))
1652+
return true;
16491653

16501654
// Fully applying an async function may suspend the caller.
16511655
if (auto applySite = FullApplySite::isa(const_cast<SILInstruction*>(this))) {

lib/SILGen/ExecutorBreadcrumb.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,11 @@ class ExecutorBreadcrumb {
3737
// Emits the hop back sequence, if any, necessary to get back to
3838
// the executor represented by this breadcrumb.
3939
void emit(SILGenFunction &SGF, SILLocation loc);
40+
41+
#ifndef NDEBUG
42+
// FOR ASSERTS ONLY: returns true if calling `emit` will emit a hop-back.
43+
bool needsEmit() const { return mustReturnToExecutor; }
44+
#endif
4045
};
4146

4247
} // namespace Lowering

lib/SILGen/ManagedValue.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,20 +58,20 @@ ManagedValue ManagedValue::copy(SILGenFunction &SGF, SILLocation loc) const {
5858

5959
// Emit an unmanaged copy of this value
6060
// WARNING: Callers of this API should manage the cleanup of this value!
61-
ManagedValue ManagedValue::unmanagedCopy(SILGenFunction &SGF,
61+
SILValue ManagedValue::unmanagedCopy(SILGenFunction &SGF,
6262
SILLocation loc) const {
6363
auto &lowering = SGF.getTypeLowering(getType());
6464
if (lowering.isTrivial())
65-
return *this;
65+
return getValue();
6666

6767
if (getType().isObject()) {
6868
auto copy = SGF.B.emitCopyValueOperation(loc, getValue());
69-
return ManagedValue::forUnmanaged(copy);
69+
return copy;
7070
}
7171

7272
SILValue buf = SGF.emitTemporaryAllocation(loc, getType());
7373
SGF.B.createCopyAddr(loc, getValue(), buf, IsNotTake, IsInitialization);
74-
return ManagedValue::forUnmanaged(buf);
74+
return buf;
7575
}
7676

7777
/// Emit a copy of this value with independent ownership.

lib/SILGen/ManagedValue.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -280,7 +280,7 @@ class ManagedValue {
280280

281281
/// Returns an unmanaged copy of this value.
282282
/// WARNING: Callers of this API should manage the cleanup of this value!
283-
ManagedValue unmanagedCopy(SILGenFunction &SGF, SILLocation loc) const;
283+
SILValue unmanagedCopy(SILGenFunction &SGF, SILLocation loc) const;
284284

285285
/// Emit a copy of this value with independent ownership into the current
286286
/// formal evaluation scope.

lib/SILGen/SILGenApply.cpp

Lines changed: 32 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4430,6 +4430,27 @@ class FixLifetimeDestroyCleanup : public Cleanup {
44304430
#endif
44314431
}
44324432
};
4433+
4434+
class EmitBreadcrumbCleanup : public Cleanup {
4435+
ExecutorBreadcrumb breadcrumb;
4436+
4437+
public:
4438+
EmitBreadcrumbCleanup(ExecutorBreadcrumb &&breadcrumb)
4439+
: breadcrumb(std::move(breadcrumb)) {}
4440+
4441+
void emit(SILGenFunction &SGF, CleanupLocation l,
4442+
ForUnwind_t forUnwind) override {
4443+
breadcrumb.emit(SGF, l);
4444+
}
4445+
4446+
void dump(SILGenFunction &SGF) const override {
4447+
#ifndef NDEBUG
4448+
llvm::errs() << "EmitBreadcrumbCleanup "
4449+
<< "State:" << getState()
4450+
<< "NeedsEmit:" << breadcrumb.needsEmit();
4451+
#endif
4452+
}
4453+
};
44334454
} // end anonymous namespace
44344455

44354456
//===----------------------------------------------------------------------===//
@@ -4589,7 +4610,7 @@ RValue SILGenFunction::emitApply(
45894610
// generates `await_async_continuation`.
45904611
// Lifetime is extended by creating unmanaged copies here and by pushing the
45914612
// cleanups required just before the result plan is generated.
4592-
SmallVector<ManagedValue, 8> unmanagedCopies;
4613+
SmallVector<SILValue, 8> unmanagedCopies;
45934614
if (calleeTypeInfo.foreign.async) {
45944615
for (auto arg : args) {
45954616
if (arg.hasCleanup()) {
@@ -4676,32 +4697,23 @@ RValue SILGenFunction::emitApply(
46764697
*foreignError, calleeTypeInfo.foreign.async);
46774698
}
46784699

4679-
// For objc async calls, push cleanup to be used on throw paths in the result
4680-
// planner.
4681-
for (unsigned i : indices(unmanagedCopies)) {
4682-
SILValue value = unmanagedCopies[i].getValue();
4683-
Cleanups.pushCleanup<FixLifetimeDestroyCleanup>(value);
4684-
unmanagedCopies[i] = ManagedValue(value, Cleanups.getTopCleanup());
4700+
// For objc async calls, push cleanups to be used on
4701+
// both result and throw paths prior to finishing the result plan.
4702+
if (calleeTypeInfo.foreign.async) {
4703+
for (auto unmanagedCopy : unmanagedCopies) {
4704+
Cleanups.pushCleanup<FixLifetimeDestroyCleanup>(unmanagedCopy);
4705+
}
4706+
// save breadcrumb as a clean-up so it is emitted in result / throw cases.
4707+
Cleanups.pushCleanup<EmitBreadcrumbCleanup>(std::move(breadcrumb));
4708+
} else {
4709+
assert(unmanagedCopies.empty());
46854710
}
46864711

46874712
auto directResultsArray = makeArrayRef(directResults);
46884713
RValue result = resultPlan->finish(*this, loc, substResultType,
46894714
directResultsArray, bridgedForeignError);
46904715
assert(directResultsArray.empty() && "didn't claim all direct results");
46914716

4692-
// For objc async calls, generate cleanup on the resume path here and forward
4693-
// the previously pushed cleanups.
4694-
if (calleeTypeInfo.foreign.async) {
4695-
for (auto unmanagedCopy : unmanagedCopies) {
4696-
auto value = unmanagedCopy.forward(*this);
4697-
B.emitFixLifetime(loc, value);
4698-
B.emitDestroyOperation(loc, value);
4699-
}
4700-
4701-
// hop back to the current executor
4702-
breadcrumb.emit(*this, loc);
4703-
}
4704-
47054717
return result;
47064718
}
47074719

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// RUN: not --crash %target-sil-opt %s 2>&1 | %FileCheck %s
2+
// REQUIRES: asserts
3+
4+
// CHECK: cannot suspend async task while unawaited continuation is active
5+
6+
sil_stage raw
7+
8+
import Builtin
9+
import Swift
10+
import SwiftShims
11+
import _Concurrency
12+
13+
// hello(_:)
14+
sil hidden [ossa] @$s4main5helloyS2bYaF : $@convention(thin) @async (Bool) -> Bool {
15+
bb0(%0 : $Bool):
16+
debug_value %0 : $Bool, let, name "b", argno 1
17+
%2 = enum $Optional<Builtin.Executor>, #Optional.none!enumelt
18+
%22 = alloc_stack $Bool
19+
%27 = get_async_continuation_addr Bool, %22 : $*Bool
20+
hop_to_executor %2 : $Optional<Builtin.Executor> // <- the bad nested suspension
21+
await_async_continuation %27 : $Builtin.RawUnsafeContinuation, resume bb1
22+
23+
bb1:
24+
dealloc_stack %22 : $*Bool
25+
return %0 : $Bool
26+
}

test/SILGen/objc_async.swift

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,9 +195,49 @@ func testSlowServerFromMain(slowServer: SlowServer) async throws {
195195
// CHECK: await_async_continuation [[CONT]] {{.*}}, resume [[RESUME:bb[0-9]+]]
196196
// CHECK: [[RESUME]]:
197197
// CHECK: [[RESULT:%.*]] = load [trivial] [[RESUME_BUF]]
198+
// CHECK: hop_to_executor %6 : $MainActor
198199
// CHECK: fix_lifetime [[COPY]]
199200
// CHECK: destroy_value [[COPY]]
200-
// CHECK: hop_to_executor %6 : $MainActor
201201
// CHECK: dealloc_stack [[RESUME_BUF]]
202202
let _: Int = await slowServer.doSomethingSlow("mail")
203203
}
204+
205+
// CHECK-LABEL: sil {{.*}}@${{.*}}26testThrowingMethodFromMain
206+
@MainActor
207+
func testThrowingMethodFromMain(slowServer: SlowServer) async -> String {
208+
// CHECK: [[RESULT_BUF:%.*]] = alloc_stack $String
209+
// CHECK: [[STRING_ARG:%.*]] = apply {{%.*}}({{%.*}}) : $@convention(method) (@guaranteed String) -> @owned NSString
210+
// CHECK: [[METH:%.*]] = objc_method {{%.*}} : $SlowServer, #SlowServer.doSomethingDangerous!foreign
211+
// CHECK: [[RAW_CONT:%.*]] = get_async_continuation_addr [throws] String, [[RESULT_BUF]] : $*String
212+
// CHECK: [[CONT:%.*]] = struct $UnsafeContinuation<String, Error> ([[RAW_CONT]] : $Builtin.RawUnsafeContinuation)
213+
// CHECK: [[STORE_ALLOC:%.*]] = alloc_stack $@block_storage UnsafeContinuation<String, Error>
214+
// CHECK: [[PROJECTED:%.*]] = project_block_storage [[STORE_ALLOC]] : $*@block_storage
215+
// CHECK: store [[CONT]] to [trivial] [[PROJECTED]] : $*UnsafeContinuation<String, Error>
216+
// CHECK: [[INVOKER:%.*]] = function_ref @$sSo8NSStringCSgSo7NSErrorCSgIeyByy_SSTz_
217+
// CHECK: [[BLOCK:%.*]] = init_block_storage_header [[STORE_ALLOC]] {{.*}}, invoke [[INVOKER]]
218+
// CHECK: [[OPTIONAL_BLK:%.*]] = enum {{.*}}, #Optional.some!enumelt, [[BLOCK]]
219+
// CHECK: %28 = apply [[METH]]([[STRING_ARG]], [[OPTIONAL_BLK]], {{%.*}}) : $@convention(objc_method) (NSString, Optional<@convention(block) (Optional<NSString>, Optional<NSError>) -> ()>, SlowServer) -> ()
220+
// CHECK: [[STRING_ARG_COPY:%.*]] = copy_value [[STRING_ARG]] : $NSString
221+
// CHECK: dealloc_stack [[STORE_ALLOC]] : $*@block_storage UnsafeContinuation<String, Error>
222+
// CHECK: destroy_value [[STRING_ARG]] : $NSString
223+
// CHECK: await_async_continuation [[RAW_CONT]] : $Builtin.RawUnsafeContinuation, resume [[RESUME:bb[0-9]+]], error [[ERROR:bb[0-9]+]]
224+
225+
// CHECK: [[RESUME]]
226+
// CHECK: {{.*}} = load [take] [[RESULT_BUF]] : $*String
227+
// CHECK: hop_to_executor {{%.*}} : $MainActor
228+
// CHECK: fix_lifetime [[STRING_ARG_COPY]] : $NSString
229+
// CHECK: destroy_value [[STRING_ARG_COPY]] : $NSString
230+
// CHECK: dealloc_stack [[RESULT_BUF]] : $*String
231+
232+
// CHECK: [[ERROR]]
233+
// CHECK: hop_to_executor {{%.*}} : $MainActor
234+
// CHECK: fix_lifetime [[STRING_ARG_COPY]] : $NSString
235+
// CHECK: destroy_value [[STRING_ARG_COPY]] : $NSString
236+
// CHECK: dealloc_stack [[RESULT_BUF]] : $*String
237+
238+
do {
239+
return try await slowServer.doSomethingDangerous("run-with-scissors")
240+
} catch {
241+
return "none"
242+
}
243+
}

test/SILGen/objc_async_from_swift.swift

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,19 +25,28 @@ func testSlowServing(p: SlowServing) async throws {
2525
// CHECK: hop_to_executor [[GENERIC_EXECUTOR]] :
2626
let _: String = await p.requestString()
2727

28-
// CHECK: objc_method {{.*}} $@convention(objc_method) <τ_0_0 where τ_0_0 : SlowServing> (@convention(block) (Optional<NSString>, Optional<NSError>) -> (), τ_0_0) -> ()
29-
// CHECK: hop_to_executor [[GENERIC_EXECUTOR]] :
30-
let _: String = try await p.tryRequestString()
31-
3228
// CHECK: objc_method {{.*}} $@convention(objc_method) <τ_0_0 where τ_0_0 : SlowServing> (@convention(block) (Int, NSString) -> (), τ_0_0) -> ()
3329
// CHECK: hop_to_executor [[GENERIC_EXECUTOR]] :
3430
let _: (Int, String) = await p.requestIntAndString()
3531

3632
// CHECK: objc_method {{.*}} $@convention(objc_method) <τ_0_0 where τ_0_0 : SlowServing> (@convention(block) (Int, Optional<NSString>, Optional<NSError>) -> (), τ_0_0) -> ()
3733
// CHECK: hop_to_executor [[GENERIC_EXECUTOR]] :
34+
// CHECK: builtin "willThrow"
35+
// CHECK-NEXT: hop_to_executor [[GENERIC_EXECUTOR]] :
3836
let _: (Int, String) = try await p.tryRequestIntAndString()
3937
}
4038

39+
// CHECK-LABEL: sil {{.*}}@{{.*}}20testSlowServingAgain
40+
func testSlowServingAgain(p: SlowServing) async throws {
41+
// CHECK: [[GENERIC_EXECUTOR:%.*]] = enum $Optional<Builtin.Executor>, #Optional.none
42+
// CHECK: hop_to_executor [[GENERIC_EXECUTOR]] :
43+
// CHECK: objc_method {{.*}} $@convention(objc_method) <τ_0_0 where τ_0_0 : SlowServing> (@convention(block) (Optional<NSString>, Optional<NSError>) -> (), τ_0_0) -> ()
44+
// CHECK: hop_to_executor [[GENERIC_EXECUTOR]] :
45+
// CHECK: builtin "willThrow"
46+
// CHECK-NEXT: hop_to_executor [[GENERIC_EXECUTOR]] :
47+
let _: String = try await p.tryRequestString()
48+
}
49+
4150
class SlowSwiftServer: NSObject, SlowServing {
4251
// CHECK-LABEL: sil {{.*}} @$s21objc_async_from_swift15SlowSwiftServerC10requestIntSiyYaF
4352
// CHECK: [[GENERIC_EXECUTOR:%.*]] = enum $Optional<Builtin.Executor>, #Optional.none

0 commit comments

Comments
 (0)