[llvm-dev] sret read after unwind

Johannes Doerfert via llvm-dev
2022-01-03T17:33:25Z

I somewhat missed this thread and while I should maybe respond
to a few of the other mails too I figured I start with a conceptual
question I have reading this:

Who and when is the attribute added? If it is implied by sret that's
a good start. For the non-sret deduction it seems very specialized.
I mean, now we have something for the unwind case but not a different
"early exit" or if it is read/writeonly rather than readnone.

The argument about invoke vs. call instruction call sites only holds for
sret args anyway, so maybe what you are designing here is too sret specific.

Long term I'd like us to have a proper "side-effect" encoding with
values
and that could include conditions, e.g.,

```
sideeffects(       write(_unknown_, %arg), read(_unknown_),
             unwind{write(_unknown_), read(_unknown_)},
             cond(load %arg eq 0, {read($arg)})
            )
```

While this is still long away (probably), I'm not convinced an attribute
that is specific to unwind *and* readnone is the right intermediate step.
It should compose better with readonly/writeonly/readnone at least.

All that said, would your deduction strategy alone solve the problem?
So, the cases you care about could they be optimized by looking at the
call sites and determining if none is an invoke?

~ Johannes


On 12/4/21 04:39, Nikita Popov via llvm-dev wrote:
> Hi,
>
> Consider the following IR:
>
> declare void @may_unwind()
> define void @test(i32* noalias sret(i32) %out) {
>      store i32 0, i32* %out
>      call void @may_unwind()
>      store i32 1, i32* %out
>      ret void
> }
>
> Currently, we can't remove the first store as dead, because the
> @may_unwind() call may unwind, and the caller might read %out at that
> point, making the first store visible.
>
> Similarly, it prevents call slot optimization in the following example,
> because the call may unwind and make an early write to the sret argument
> visible:
>
> declare void @may_unwind(i32*)
> declare void @llvm.memcpy.p0i8.p0i8.i64(i8*, i8*, i64, i1)
> define void @test(i32* noalias sret(i32) %arg) {
>      %tmp = alloca i32
>      call void @may_unwind(i32* nocapture %tmp)
>      %tmp.8 = bitcast i32* %tmp to i8*
>      %arg.8 = bitcast i32* %arg to i8*
>      call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %arg.8, i8* align 4
> %tmp.8, i64 4, i1 false)
>      ret void
> }
>
> I would like to address this in some form. The easiest way would be to
> change LangRef to specify that sret arguments cannot be read on unwind
> paths. I think that matches how sret arguments are generally used.
>
> Alternatively, this could be handled using a separate attribute that can be
> applied to any argument, something along the lines of "i32*
nounwindread
> sret(i32) %arg". The benefit would be that this is decoupled from sret
ABI
> semantics and could potentially be inferred (e.g. if the function is only
> ever used with call and not invoke, this should be a given).
>
> Any thoughts on this? Is this a problem worth solving, and if yes, would a
> new attribute be preferred over restricting sret semantics?