[llvm-dev] sret read after unwind

Nikita Popov via llvm-dev llvm-dev at lists.llvm.org
Tue Jan 4 01:39:26 PST 2022


On Mon, Jan 3, 2022 at 6:33 PM Johannes Doerfert <johannesdoerfert at gmail.com>
wrote:

> 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.
>

I'm mainly interested in frontend-annotated cases here, rather than deduced
ones. The primary use case there is adding it to sret arguments (and only
changing sret semantics would be "good enough" for me, I guess). However,
there is another frontend-annotated case I have my eyes on, which is move
arguments in rust. These could be modeled by a combination of
noreadonunwind and noreadonreturn to indicate that the value will not be
used after the call at all, regardless of how it exits. (This would be kind
of similar to a byval argument, just without the ABI implication that an
actual copy gets inserted.)

Note that as proposed, the noreadonunwind attribute would be the "writeonly
on unwind" combination (and noreadonreturn the "writeonly on return"
combination). I can see that there are conjugated "readonly on unwind" and
"readonly on return" attributes that could be defined here, but I can't
think of any circumstances under which these would actually be useful for
optimization purposes. How would the presence or absence of later writes
impact optimization in the current function?

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.
>

Not sure I follow, why would that argument only hold for sret?

Regards,
Nikita


> 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?
> >
> > Regards,
> > Nikita
> >
> >
> > _______________________________________________
> > LLVM Developers mailing list
> > llvm-dev at lists.llvm.org
> > https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20220104/d77fc9e7/attachment.html>


More information about the llvm-dev mailing list