This is an archive of the discontinued LLVM Phabricator instance.

[Coroutines] [Retcon] Replace CoroBegin with FramePtr after
AbandonedPublic

Authored by ChuanqiXu on Mar 24 2022, 4:45 AM.

Details

Reviewers
rjmccall
Group Reviewers
Restricted Project
Summary

This tries to fix issue https://github.com/llvm/llvm-project/issues/54523 by repalcing CoroBegin with FramePtr later in splitRetconCoroutine.

Diff Detail

Event Timeline

ChuanqiXu created this revision.Mar 24 2022, 4:45 AM
Herald added a project: Restricted Project. · View Herald TranscriptMar 24 2022, 4:45 AM
Herald added a subscriber: hiraditya. · View Herald Transcript
ChuanqiXu requested review of this revision.Mar 24 2022, 4:45 AM
Herald added a project: Restricted Project. · View Herald TranscriptMar 24 2022, 4:45 AM
nikic added reviewers: rjmccall, Restricted Project.Mar 28 2022, 1:33 AM
nikic added a subscriber: nikic.

Thanks! I like this fix more than D122375, but I'm not familiar with the details of this code.

Hmm. Doing this after doing the clones means you're only actually doing the replacement in the ramp function, which I think means it'll explode in the clones if there are uses remaining. On some level, that might be okay, because I don't think the Swift frontend ever actually uses the result of coro.begin in retcon functions except to pass it to coro.end, and retcon lowering of coro.end just ignores the argument. On the other hand, we shouldn't leave something around that'll blow up if the code pattern ever changes.

I think you can just recognize this case (when Shape.FramePtr is an argument) and remove the mapping for that argument from VMap before cloning.

Hmm. Doing this after doing the clones means you're only actually doing the replacement in the ramp function, which I think means it'll explode in the clones if there are uses remaining. On some level, that might be okay, because I don't think the Swift frontend ever actually uses the result of coro.begin in retcon functions except to pass it to coro.end, and retcon lowering of coro.end just ignores the argument. On the other hand, we shouldn't leave something around that'll blow up if the code pattern ever changes.

I think you can just recognize this case (when Shape.FramePtr is an argument) and remove the mapping for that argument from VMap before cloning.

I found another idea. Here I found the second argument of coro.begin is meant to point to the coroutine frame. (https://llvm.org/docs/Coroutines.html#llvm-coro-begin-intrinsic) I found it says that it is ignored by returned-continuation coroutines. But I think it is for the frontend users. We don't invalidate it if we set it in the middle end. And if we could do it, then the codes in CoroCleanup would replace coro.begin with the second argument. Do you think this is available? If yes, I think it might be better than handle the argument case specially.

llvm/lib/Transforms/Coroutines/CoroSplit.cpp
1777

How about setting the memory argument for CoroBegin here:

Shape.CoroBegin->setOperand(1, RawFramePtr);

Then the CoroBegin would be replaced to RawFramePtr later in https://github.com/llvm/llvm-project/blob/8568d681ae14fb8c7769c80e0196a16934647413/llvm/lib/Transforms/Coroutines/CoroCleanup.cpp#L67-L69

Does that actually fix anything? The problem is that the use-list of the frame pointer is being destroyed in the clones if it happens to be an argument of the ramp function. We have to stop that from happening. I don't see a solution to that other than either changing VMap or introducing an intermediate value so that it isn't just a use of an argument, and the former seems much easier.

Does that actually fix anything? The problem is that the use-list of the frame pointer is being destroyed in the clones if it happens to be an argument of the ramp function. We have to stop that from happening. I don't see a solution to that other than either changing VMap or introducing an intermediate value so that it isn't just a use of an argument, and the former seems much easier.

I think the two strategies (Move the replacement of CoroBegin or set the memory argument for CoroBegin) aim to the solution introducing an intermediate value so that it isn't just a use of an argument, and the intermediate value here is the llvm.coro.begin itself. I think this one if more natural to changing VMap since it might be a noop for other coroutine ABIs.

It seems very likely to me that you could contrive a situation under C++ coroutines where the frame pointer is a parameter to the ramp function by the time you run coro splitting, at least under optimization. For example, https://godbolt.org/z/cGnjTbzrY, which is a reasonable implementation if you need to guarantee non-allocation and intend to assert that the size fits in the buffer. This isn't an oddity of retcon lowering, it's a general problem that emerges from the special way that the frame pointer value is assumed to be live throughout the function, which requires it to be protected against this undef'ing of arguments which would otherwise be reasonable.

Thinking about it further, you'll probably have to introduce an intermediate value to do that protection, since the cloner will not be happy if you don't map all the arguments.

nikic added a comment.Mar 30 2022, 3:08 AM

It seems very likely to me that you could contrive a situation under C++ coroutines where the frame pointer is a parameter to the ramp function by the time you run coro splitting, at least under optimization. For example, https://godbolt.org/z/cGnjTbzrY, which is a reasonable implementation if you need to guarantee non-allocation and intend to assert that the size fits in the buffer. This isn't an oddity of retcon lowering, it's a general problem that emerges from the special way that the frame pointer value is assumed to be live throughout the function, which requires it to be protected against this undef'ing of arguments which would otherwise be reasonable.

Thinking about it further, you'll probably have to introduce an intermediate value to do that protection, since the cloner will not be happy if you don't map all the arguments.

@rjmccall So would D122375 be about what you have in mind here?

Yeah. I think you could probably get away with just doing to the FramePtr argument, but it's probably safer to do it unconditionally.

ChuanqiXu abandoned this revision.Mar 30 2022, 7:06 PM

It seems very likely to me that you could contrive a situation under C++ coroutines where the frame pointer is a parameter to the ramp function by the time you run coro splitting, at least under optimization. For example, https://godbolt.org/z/cGnjTbzrY, which is a reasonable implementation if you need to guarantee non-allocation and intend to assert that the size fits in the buffer. This isn't an oddity of retcon lowering, it's a general problem that emerges from the special way that the frame pointer value is assumed to be live throughout the function, which requires it to be protected against this undef'ing of arguments which would otherwise be reasonable.

Thinking about it further, you'll probably have to introduce an intermediate value to do that protection, since the cloner will not be happy if you don't map all the arguments.

Yeah, your words make sense. Let's abandon this one.