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
-------------- next part --------------
An HTML attachment was scrubbed...
URL:
<http://lists.llvm.org/pipermail/llvm-dev/attachments/20211204/c830a34c/attachment.html>
I think this is an interesting problem. I'd probably lean towards the use of a separate attribute, but not strongly so. The example which makes me prefer the separate attribute would be a function with an out-param. It's very unlikely that an out-param will be read on the exception path. Being able to perform DSE for such out params seems quite interesting. However, I'll note that the same problem can be framed as an escape problem. That is, we have an annotation not that a value is dead on the exception path, but that it hasn't been captured on entry to the routine. Then, we can apply local reasoning to show that the first store can't be visible to may_unwind, and eliminate it. I'd want to give the escape framing more thought as that seems potentially more general. Does knowing that an argument does not point to escaped memory on entry help on all of your motivating examples? Philip On 12/4/21 2:39 AM, 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/20211206/84a0620b/attachment.html>
I ran across something a bit similar, and thought I'd share the case for
purposes of idea generation.
For a function w/out-params, it's common to have cases where the
out-params are not actually used by the callee. I've recently been
making some improvements for the cases where the out-param is the only
thing holding the call live (D115829), but if we actually use the return
value, we're left with a dead write (inside the callee) which we can't
seem to eliminate without inlining.
As an example:
declare i1 @callee(i32* %out) {
store i32 1, i32* %out
ret i1 true
}
declare void @test() {
%a = alloca i32
%res = call i1 @callee(i32* %a)
call void @use(%res)
}
If we had similar in spirit to your "nounwindread" but applied to the
normal return path (e.g. "noreadonreturn"), we could in principal
leverage this to simplify the callee. DSE has all the information today
to annotate "noreadonreturn" arguments at the call site. We could
have
an IPO transform which merges the information from all callees, and
drops the writes. (We could also e.g. specialize if not all had the
param as dead.)
This particular case isn't strongly motivated enough to bother building
out infrastructure for, but it's interesting that another somewhat
analogous use case has popped up.
Philip
On 12/4/21 2:39 AM, 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/20211216/9a1353ce/attachment.html>
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?
>
> Regards,
> Nikita
>
>
> _______________________________________________
> LLVM Developers mailing list
> llvm-dev at lists.llvm.org
> https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev