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.)
OK. That’s interesting. I’m not fluent enough in rust, can you
elaborate what the semantics there would be, maybe an IR example?
To give a silly example, take these two functions in rust (https://rust.godbolt.org/z/9cvefedsP):
pub fn test1(mut s: String) {
s = “foobar”.to_string();
}
pub fn test2(s: &mut String) {
*s = “foobar”.to_string();
}
From an ABI perspective, these are basically the same. In both cases rust will lower this to passing in a String*. However, because String is a non-Copy type, any call “test(s)” will move the “s” variable, which means that “s” cannot be used after the call anymore. For that reason, the store “s =” would be definitely dead in the first example, and usually not dead in the second example.
Spitballing: byval(nocopy, %a)
might be worth thinking about
given the short description.
Yeah, I guess that would work – though I’d rather not mix ABI and optimization attributes in that way…
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?
Just as an example, readonly on unwind
allows you to do GVN/CSE
from prior to the call to the “unwind path”. Return then on the
“return path”. That is not inside the call but in the caller.
Does that make sense?
Let me check if I understood the idea right: We have a invoke with a hypothetical “readonly on unwind” / “no write on unwind” attribute. In the landing pad, there is a non-analyzable write and the pointer has previously escaped, and then later there is a read from the argument. The non-analyzable write blocks AA, while the “readonly on unwind” guarantee could make a sufficiently smart AA see that this write cannot write into the argument memory. Is that the idea? I feel like “readonly on unwind” isn’t the right way to model that situation, rather the argument could be marked as invariant in the unwind path using one of our existing ways to denote invariance.
But I suspect I still didn’t quite get what you have in mind here. An example would help.
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?
static void I_will_unwind(int *A) {
*A = 42;
may_unwind();
*A = 4711;
unwind();
}
void someone_might_catch_me_as_I_also_unwind(int *A) {
/* call */ I_will_unwind(A);
}
Maybe I misunderstood your idea but doesn’t the above show how
we have only call instruction call sites and we still cannot
assume the store is dead on the unwind path? If you check
transitively throughout the entire call chain it’s different,
but that is not how I read your first mail. I figured it works
for sret because the memory does not outlive the caller.
Ah yes, this was imprecise in the original mail. We need that a) it’s only used with call (if we don’t want to analyze the unwind paths to be more precise) and b) is noreadonunwind itself. Where the latter might be because it’s based on an argument with that attribute, or because it’s an alloca, which is always noreadonunwind.
Regards,
Nikita