[PATCH] D18738: Add new !unconditionally_dereferenceable load instruction metadata
Sanjoy Das via llvm-commits
llvm-commits at lists.llvm.org
Thu Nov 10 13:47:43 PST 2016
sanjoy added a comment.
In https://reviews.llvm.org/D18738#592124, @whitequark wrote:
> @sanjoy Your example is being transformed as expected, from my POV. The idea is that an unconditionally dereferenceable load cannot be moved across a store that may-alias the pointer, and this lets one initialize it safely. Other than immediately after allocation, a pointer which is ever loaded like that must always be dereferenceable.
(Sorry for the wall of text, but I've repeated things here to consolidate some of the previous arguments and present something coherent.)
Maybe we are talking about slightly different things, but I'm trying to avoid adding things to the IR that affect the IR semantics //without// executing. That is, the optimizer should be able to add `if (false) { /* Whatever it wants as long as it syntactically correct. */ }` and have it not affect what the program's behavior. I don't think there are any constructs in the IR today that allow this, and if there are, we should try to fix them, and not add more.
The way dead code can affect behavior using this construct is:
(snippet 1)
if (false) {
%t0 = alloca i32*
%t1 = load i32*, i32** %t0, !uncond_deref
%t2 = load i32, i32* %t1
}
given the rules you're suggesting (IIUC) can be transformed to
%t0 = alloca i32*
%t1 = load i32*, i32** %t0, !uncond_deref
%t2 = load i32, i32* %t1
if (false) {
}
which will introduce a fault in the program.
I want to avoid the "dead-code-affects-semantics" problem because I think it will make the IR difficult to reason about and optimize. For instance, say we want to do some form of "checked devirtualization" (you compare the vtable of the receiver with some constant, and if that comparison succeeds, you do a direct call which can then be inlined alter, else you do a normal virtual call). That is:
func_ptr = rcvr->vtable[5];
func_ptr(40);
to
t = rcvr->vtable;
if (t == __STRING__) {
foo(40);
} else {
func_ptr = t[5];
func_ptr(40);
}
The justification for the optimization above is that in case `t` is not `== __STRING__` then the call to `foo(40)` is dead and won't affect the behavior of the program, and if `t` is `== __STRING__` then you'd have called `foo` anyway. However, if the body of `foo` is (snippet 1) then the optimizer could end up introducing a fault in the program that wasn't there before after inlining foo.
Another example is here: https://reviews.llvm.org/D18738#390736
Repository:
rL LLVM
https://reviews.llvm.org/D18738
More information about the llvm-commits
mailing list