This is an archive of the discontinued LLVM Phabricator instance.

CoroFrame: Put escaped variables with multiple lifetimes on coroutine frame
ClosedPublic

Authored by MatzeB on Dec 16 2022, 10:44 AM.

Details

Summary

The llvm.lifetime.start intrinsic guarantees that the address for a
given alloca is always the same. So variables with escaped addresses
reaching reaching a lifetime start/end block before and after a suspend
must be placed onto the coroutine frame even if the variable itself
is not alive across the suspend point.

This computes a new LoopKill flag in the suspend crossing data flow
anaysis to catch the case where a lifetime marker can reach itself
via suspend-crossing path.

This fixes https://llvm.org/PR52501

Diff Detail

Event Timeline

MatzeB created this revision.Dec 16 2022, 10:44 AM
Herald added a project: Restricted Project. · View Herald TranscriptDec 16 2022, 10:44 AM
MatzeB requested review of this revision.Dec 16 2022, 10:44 AM
Herald added a project: Restricted Project. · View Herald TranscriptDec 16 2022, 10:44 AM
ChuanqiXu accepted this revision.EditedDec 18 2022, 7:40 PM

The patch is self-contained and good.

I am curious about why https://github.com/llvm/llvm-project/issues/51843 is arm related since the patch doesn't involve the backend.

And another point is about the assumption:

The llvm.lifetime.start intrinsic guarantees that the address for a given alloca is always the same.

In the manual (https://llvm.org/docs/LangRef.html#int-lifestart), it says:

After llvm.lifetime.end is called, ‘llvm.lifetime.start’ on the stack object can be called again. The second ‘llvm.lifetime.start’ call marks the object as alive, but it does not change the address of the object.

I want to say these two statements doesn't look the same. Maybe it'll be better to send another patch to edit the manual too?

llvm/lib/Transforms/Coroutines/CoroFrame.cpp
82–84

Is this change necessary? I mean if there is a path that can reach block 'i' and repeating 'i' , we must can reduce a path which can reach 'I' without repeating 'I'. Do I misunderstand anything?

This revision is now accepted and ready to land.Dec 18 2022, 7:40 PM

The patch is self-contained and good.

I am curious about why https://github.com/llvm/llvm-project/issues/51843 is arm related since the patch doesn't involve the backend.

The issues is somewhat accidentally hitting ARM, because for the program in question passing a struct with 2 members, is modeled as 2xi64 vector type by the arm calling convention. This means there are instructions constructing this 2xi64 vector and those happen to get hoisted out of a loop. 1 of those 2 values is the address of a local variable (casted to i64 because of the calling convention) so the address suddenly needs to be constant even across the resume point within the loop and even though the variable is not alive throughout the whole loop as marked by the lifetime.start/end intrinsics.
The issue does not manifest on x86_64 because the struct calling convention uses 2 (separate) integer values instead of constructing a vector and those are simple enough to not get hoisted out of the loop it seems).

And another point is about the assumption:

The llvm.lifetime.start intrinsic guarantees that the address for a given alloca is always the same.

I expressed this a bit sloppily. It's always the same for a single call (or for a coroutine a single call/ramp-up and all following suspendends and continuations. It obviously doesn't need to be the same for separate calls.

In the manual (https://llvm.org/docs/LangRef.html#int-lifestart), it says:

After llvm.lifetime.end is called, ‘llvm.lifetime.start’ on the stack object can be called again. The second ‘llvm.lifetime.start’ call marks the object as alive, but it does not change the address of the object.

I want to say these two statements doesn't look the same. Maybe it'll be better to send another patch to edit the manual too?

The manual is fine as-is and doesn't need a change, doesn't it?

PS: I am hearing reports that my fix repairs some of the problems our users had but not all of them, so I will do some more testing and investigation before landing this.

llvm/lib/Transforms/Coroutines/CoroFrame.cpp
82–84

I am clarifying the definition for a case like the following and a query whether "B kills B":

A
↓
B           ← ←
↓             ↑
C (suspend) → ↑
↓
D

If you queried whether the path from "B" to "B" crosses a suspend point, the "Kill" flag flags compute here reported false. This result does ignore the path around the loop B->C->B though, which is another way to reach B and contains a suspense point in C.

For most users in this file this interpretation is fine as it compares a "Def" and a "Use" and both being in the same block, just means the Def and the Use is in the same block and it doesn't matter that there we could also reach the block with a loop. (...And we may or not be right in this assumption that the "Def" always comes before the "Use" in the same block; but that is a discussion of corner cases for another day...)

In the case of this change however when comparing lifetime.start values however we do need to test whether any path from a lifetime.start to a lifetime.start contains a suspend point even if it is the same point reached through a loop. Hence I introduce the new KillLoop flag here which captures this loop situation as the "Kills" flag alone does not catch it.

The patch is self-contained and good.

I am curious about why https://github.com/llvm/llvm-project/issues/51843 is arm related since the patch doesn't involve the backend.

The issues is somewhat accidentally hitting ARM, because for the program in question passing a struct with 2 members, is modeled as 2xi64 vector type by the arm calling convention. This means there are instructions constructing this 2xi64 vector and those happen to get hoisted out of a loop. 1 of those 2 values is the address of a local variable (casted to i64 because of the calling convention) so the address suddenly needs to be constant even across the resume point within the loop and even though the variable is not alive throughout the whole loop as marked by the lifetime.start/end intrinsics.
The issue does not manifest on x86_64 because the struct calling convention uses 2 (separate) integer values instead of constructing a vector and those are simple enough to not get hoisted out of the loop it seems).

Thanks for the explanation.

And another point is about the assumption:

The llvm.lifetime.start intrinsic guarantees that the address for a given alloca is always the same.

I expressed this a bit sloppily. It's always the same for a single call (or for a coroutine a single call/ramp-up and all following suspendends and continuations. It obviously doesn't need to be the same for separate calls.

In the manual (https://llvm.org/docs/LangRef.html#int-lifestart), it says:

After llvm.lifetime.end is called, ‘llvm.lifetime.start’ on the stack object can be called again. The second ‘llvm.lifetime.start’ call marks the object as alive, but it does not change the address of the object.

I want to say these two statements doesn't look the same. Maybe it'll be better to send another patch to edit the manual too?

The manual is fine as-is and doesn't need a change, doesn't it?

Yes. After re-thinking about this, I think we need to do this even without lifetime markers (I'm not asking for a change). I mean we need to do this because this is the semantics of the coroutines instead of the semantics of lifetime markers. I don't require the changes since we'll always generate lifetime markers for coroutines as a helper. So we might not need to change the semantics of lifetime markers.

PS: I am hearing reports that my fix repairs some of the problems our users had but not all of them, so I will do some more testing and investigation before landing this.

Got it.

llvm/lib/Transforms/Coroutines/CoroFrame.cpp
82–84

Thanks for the explanation. But I guess you misunderstand my points. When I say this change, I mean the comment changes about Kills. I want to ask if the new comments add newer information then the old comments. Or it is just cleaner. (I'm not asking for a change.)

MatzeB added a comment.Jan 4 2023, 5:41 AM

Decided to rebase and land this now.

MatzeB updated this revision to Diff 486257.Jan 4 2023, 6:12 AM