Skip to content

Commit 56b1c7d

Browse files
authored
Merge pull request #58398 from kavon/5.7-fix-cont-jump-to-null
[5.7][🍒] Fix jump-to-null issue for ObjC async calls.
2 parents f3c2ad0 + 8b5aaae commit 56b1c7d

File tree

6 files changed

+30
-96
lines changed

6 files changed

+30
-96
lines changed

lib/SILGen/ExecutorBreadcrumb.h

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,6 @@ 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
4540
};
4641

4742
} // 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-
SILValue ManagedValue::unmanagedCopy(SILGenFunction &SGF,
61+
ManagedValue ManagedValue::unmanagedCopy(SILGenFunction &SGF,
6262
SILLocation loc) const {
6363
auto &lowering = SGF.getTypeLowering(getType());
6464
if (lowering.isTrivial())
65-
return getValue();
65+
return *this;
6666

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

7272
SILValue buf = SGF.emitTemporaryAllocation(loc, getType());
7373
SGF.B.createCopyAddr(loc, getValue(), buf, IsNotTake, IsInitialization);
74-
return buf;
74+
return ManagedValue::forUnmanaged(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-
SILValue unmanagedCopy(SILGenFunction &SGF, SILLocation loc) const;
283+
ManagedValue 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: 20 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -4427,27 +4427,6 @@ class FixLifetimeDestroyCleanup : public Cleanup {
44274427
#endif
44284428
}
44294429
};
4430-
4431-
class EmitBreadcrumbCleanup : public Cleanup {
4432-
ExecutorBreadcrumb breadcrumb;
4433-
4434-
public:
4435-
EmitBreadcrumbCleanup(ExecutorBreadcrumb &&breadcrumb)
4436-
: breadcrumb(std::move(breadcrumb)) {}
4437-
4438-
void emit(SILGenFunction &SGF, CleanupLocation l,
4439-
ForUnwind_t forUnwind) override {
4440-
breadcrumb.emit(SGF, l);
4441-
}
4442-
4443-
void dump(SILGenFunction &SGF) const override {
4444-
#ifndef NDEBUG
4445-
llvm::errs() << "EmitBreadcrumbCleanup "
4446-
<< "State:" << getState()
4447-
<< "NeedsEmit:" << breadcrumb.needsEmit();
4448-
#endif
4449-
}
4450-
};
44514430
} // end anonymous namespace
44524431

44534432
//===----------------------------------------------------------------------===//
@@ -4607,7 +4586,7 @@ RValue SILGenFunction::emitApply(
46074586
// generates `await_async_continuation`.
46084587
// Lifetime is extended by creating unmanaged copies here and by pushing the
46094588
// cleanups required just before the result plan is generated.
4610-
SmallVector<SILValue, 8> unmanagedCopies;
4589+
SmallVector<ManagedValue, 8> unmanagedCopies;
46114590
if (calleeTypeInfo.foreign.async) {
46124591
for (auto arg : args) {
46134592
if (arg.hasCleanup()) {
@@ -4694,23 +4673,32 @@ RValue SILGenFunction::emitApply(
46944673
*foreignError, calleeTypeInfo.foreign.async);
46954674
}
46964675

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

47094684
auto directResultsArray = makeArrayRef(directResults);
47104685
RValue result = resultPlan->finish(*this, loc, substResultType,
47114686
directResultsArray, bridgedForeignError);
47124687
assert(directResultsArray.empty() && "didn't claim all direct results");
47134688

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

test/SILGen/objc_async.swift

Lines changed: 1 addition & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -195,49 +195,9 @@ 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
199198
// CHECK: fix_lifetime [[COPY]]
200199
// 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: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -25,28 +25,19 @@ 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+
2832
// CHECK: objc_method {{.*}} $@convention(objc_method) <τ_0_0 where τ_0_0 : SlowServing> (@convention(block) (Int, NSString) -> (), τ_0_0) -> ()
2933
// CHECK: hop_to_executor [[GENERIC_EXECUTOR]] :
3034
let _: (Int, String) = await p.requestIntAndString()
3135

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

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-
5041
class SlowSwiftServer: NSObject, SlowServing {
5142
// CHECK-LABEL: sil {{.*}} @$s21objc_async_from_swift15SlowSwiftServerC10requestIntSiyYaF
5243
// CHECK: [[GENERIC_EXECUTOR:%.*]] = enum $Optional<Builtin.Executor>, #Optional.none

0 commit comments

Comments
 (0)