-
Notifications
You must be signed in to change notification settings - Fork 13.5k
Extend retcon.once
coroutines lowering to optionally produce a normal result
#66333
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
@llvm/pr-subscribers-clang-codegen @llvm/pr-subscribers-mlir ChangesOne of the main user of these kind of coroutines is swift. There yield-once (`retcon.once`) coroutines are used to temporary "expose" pointers to internal fields of various objects creating borrow scopes.However, in some cases it might be useful also to allow these coroutines to produce a normal result, however, there is no way to represent this (as compared to switched-resume kind of coroutines where C++ The extension is simple: we simply allow continuation function to have non-void result and accept optional extra arguments to
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One of the main user of these kind of coroutines is swift. There yield-once (retcon.once) coroutines are used to temporary "expose" pointers to internal fields of various objects creating borrow scopes.
However, in some cases it might be useful also to allow these coroutines to produce a normal result, however, there is no way to represent this (as compared to switched-resume kind of coroutines where C++ co_return is transformed to a member / callback call on promise object).
Out of curiousity, why don't we have the problem in the normal return continuation ABI?
The problem happens when the value is directly used in |
I still don't understand the motivation fully. Do you say we don't have the problem naturally? Or could you show some motivation examples? (In LLVM IR?) |
This one is problematic: define {ptr, ptr} @g(ptr %buffer, ptr %ptr, i8 %val) presplitcoroutine {
entry:
%temp = alloca i32, align 4
%id = call token @llvm.coro.id.retcon.once(i32 8, i32 8, ptr %buffer, ptr @prototype2, ptr @allocate, ptr @deallocate)
%hdl = call ptr @llvm.coro.begin(token %id, ptr null)
%oldvalue = load i32, ptr %ptr
store i32 %oldvalue, ptr %temp
%unwind = call i1 (...) @llvm.coro.suspend.retcon.i1(ptr %temp)
br i1 %unwind, label %cleanup, label %cont
cont:
%newvalue = load i32, ptr %temp
store i32 %newvalue, ptr %ptr
br label %cleanup
cleanup:
call i1 (ptr, i1, ...) @llvm.coro.end(ptr %hdl, i1 0, i8 %val)
unreachable
} but this one is not (cleanup will be split before define {ptr, ptr} @g(ptr %buffer, ptr %ptr, i8 %val) presplitcoroutine {
entry:
%temp = alloca i32, align 4
%id = call token @llvm.coro.id.retcon.once(i32 8, i32 8, ptr %buffer, ptr @prototype2, ptr @allocate, ptr @deallocate)
%hdl = call ptr @llvm.coro.begin(token %id, ptr null)
%oldvalue = load i32, ptr %ptr
store i32 %oldvalue, ptr %temp
%unwind = call i1 (...) @llvm.coro.suspend.retcon.i1(ptr %temp)
br i1 %unwind, label %cleanup, label %cont
cont:
%newvalue = load i32, ptr %temp
store i32 %newvalue, ptr %ptr
br label %cleanup
cleanup:
%newval = add i8 %val, 42
call i1 (ptr, i1, ...) @llvm.coro.end(ptr %hdl, i1 0, i8 %newval)
unreachable
} This one is problematic as well: define {ptr, ptr} @g(ptr %buffer, ptr %ptr, i8 %val) presplitcoroutine {
entry:
%temp = alloca i32, align 4
%id = call token @llvm.coro.id.retcon.once(i32 8, i32 8, ptr %buffer, ptr @prototype2, ptr @allocate, ptr @deallocate)
%hdl = call ptr @llvm.coro.begin(token %id, ptr null)
%oldvalue = load i32, ptr %ptr
store i32 %oldvalue, ptr %temp
%newval = add i8 %val, 42
%unwind = call i1 (...) @llvm.coro.suspend.retcon.i1(ptr %temp)
br i1 %unwind, label %cleanup, label %cont
cont:
%newvalue = load i32, ptr %temp
store i32 %newvalue, ptr %ptr
br label %cleanup
cleanup:
call i1 (ptr, i1, ...) @llvm.coro.end(ptr %hdl, i1 0, i8 %newval)
unreachable
} All these are "new" cases I would say, everything else is handled via current split approach. |
Oh, I am wondering if we have misunderstandings. I am not asking the reason why we don't have the problem which is discussing in the change of CoroFrame.cpp in return continuation ABI. I understand that fully. What make me curious is the motivation case of the PR. I mean what can be presented in retcon.once ABI after the PR which is impossible/hard before. And how do we handle that in retcon ABI. Sorry for confusion. |
Well, the PR allows The particular usecase from Swift is as follows:
The particular example of coroutine lowering could be seen in https://github.com/llvm/llvm-project/pull/66333/files#diff-70da911ce77807c88cdfcf3578aa2a569e131c99653488d148a5fd6e9b0df169 |
Got it. Thanks. Then I am wondering how about the |
Ah, ok. The current PR is only for |
Got it. Thanks for the explanation : ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM. Thanks.
…y) return values from `retcon.once` coroutines.
cd7b571
to
5c30195
Compare
…al result (llvm#66333) One of the main user of these kind of coroutines is swift. There yield-once (`retcon.once`) coroutines are used to temporary "expose" pointers to internal fields of various objects creating borrow scopes. However, in some cases it might be useful also to allow these coroutines to produce a normal result, but there is no convenient way to represent this (as compared to switched-resume kind of coroutines where C++ `co_return` is transformed to a member / callback call on promise object). The extension is simple: we allow continuation function to have a non-void result and accept optional extra arguments via a special `llvm.coro.end.result` intrinsic that would essentially forward them as normal results.
…al result (llvm#66333) One of the main user of these kind of coroutines is swift. There yield-once (`retcon.once`) coroutines are used to temporary "expose" pointers to internal fields of various objects creating borrow scopes. However, in some cases it might be useful also to allow these coroutines to produce a normal result, but there is no convenient way to represent this (as compared to switched-resume kind of coroutines where C++ `co_return` is transformed to a member / callback call on promise object). The extension is simple: we allow continuation function to have a non-void result and accept optional extra arguments via a special `llvm.coro.end.result` intrinsic that would essentially forward them as normal results.
…al result (llvm#66333) One of the main user of these kind of coroutines is swift. There yield-once (`retcon.once`) coroutines are used to temporary "expose" pointers to internal fields of various objects creating borrow scopes. However, in some cases it might be useful also to allow these coroutines to produce a normal result, but there is no convenient way to represent this (as compared to switched-resume kind of coroutines where C++ `co_return` is transformed to a member / callback call on promise object). The extension is simple: we allow continuation function to have a non-void result and accept optional extra arguments via a special `llvm.coro.end.result` intrinsic that would essentially forward them as normal results.
* Update coroutine intrinsics documentation and few remaining tests to opaque pointers (llvm#65698) * Extend `retcon.once` coroutines lowering to optionally produce a normal result (llvm#66333) One of the main user of these kind of coroutines is swift. There yield-once (`retcon.once`) coroutines are used to temporary "expose" pointers to internal fields of various objects creating borrow scopes. However, in some cases it might be useful also to allow these coroutines to produce a normal result, but there is no convenient way to represent this (as compared to switched-resume kind of coroutines where C++ `co_return` is transformed to a member / callback call on promise object). The extension is simple: we allow continuation function to have a non-void result and accept optional extra arguments via a special `llvm.coro.end.result` intrinsic that would essentially forward them as normal results.
One of the main user of these kind of coroutines is swift. There yield-once (
retcon.once
) coroutines are used to temporary "expose" pointers to internal fields of various objects creating borrow scopes.However, in some cases it might be useful also to allow these coroutines to produce a normal result, however, there is no way to represent this (as compared to switched-resume kind of coroutines where C++
co_return
is transformed to a member / callback call on promise object).The extension is simple: we allow continuation function to have a non-void result and accept optional extra arguments to
llvm.coro.end
intrinsic that would essentially forward them as normal results. Everything is backward compatible in terms of LLVM C++ API (as we only madellvm.coro.end
intrinsic variadic), so no changes in downstream projects are expected.