[PATCH] D20116: Add speculatable function attribute
Sanjoy Das via Phabricator via llvm-commits
llvm-commits at lists.llvm.org
Wed Mar 22 16:34:42 PDT 2017
sanjoy added a comment.
In https://reviews.llvm.org/D20116#708109, @hfinkel wrote:
> In https://reviews.llvm.org/D20116#708076, @mehdi_amini wrote:
>
> > In https://reviews.llvm.org/D20116#707954, @sanjoy wrote:
> >
> > > If we go with (2), then we've admitted that "dead code" (that is, code that is not run) can influence program behavior, which I find troubling.
> >
> >
> > That troubles (and worries) me as well.
>
>
> Why? That's part of the promise we make when we tag the code as speculatable. We promise that doing the operation early, even in cases where it might otherwise have been performed, is fine. Furthermore, we're promising that any properties the call has (e.g. promising that a certain argument is nonnull) must not be control dependent. As a result, looking at it as dead code affecting live code is suboptimal; any other properties are just a kind of global assertion.
Making even the behavior of a program dependent on instructions that are never executed seems like a fundamentally new thing, and I'm not yet convinced that that's safe. It //may// be possible to come up with a consistent model of this new thing, but I think the model will still be tricky to work with.
For instance, certain kinds of devritualization optimizations are wrong in that model. Say we had:
void f() { }
void g() { 1 / 0; } // unused
void main() {
fnptr p = f;
*p() speculatable;
}
Now you're saying you can't specialize the call via function pointer like this:
void f() { }
void g() { 1 / 0; } // unused
void main() {
fnptr p = f;
if (p == g)
g() speculatable; // incorrect
else
*p() speculatable;
}
which seems odd.
There are also (somewhat more complex) cases like this that do not involve indirect speculatable calls:
struct Base {
int k;
Base(int k) : k(k) {}
};
struct S : public Base {
S(bool c) : Base(c ? 10 : 20) {}
virtual void f() {
div(1, k) speculatable; // k is either 10 or 20
}
};
struct T : public Base {
T() : Base(0) {}
virtual void f() { }
};
void bug(Base* b) {
b->f();
}
We have a problem in `bug` if we ever devirtualize, inline and hoist a load:
void bug(Base *b) {
int k = b->k;
if (b->type == S) {
div(1, k) speculatable;
} else (b->type == T) {
}
}
It also breaks "code compression" type optimizations:
if (a == 1 || a == 2) {
switch (a) {
case 1:
div(m, 1) speculatable;
break;
case 2:
div(m, 2) speculatable;
break;
}
}
to
if (a == 1 || a == 2) {
div(m, a) speculatable;
}
to
if (a == 1 || a == 2) {
if (a == 0)
div(m, 0) speculatable;
else
div(m, a) speculatable;
}
https://reviews.llvm.org/D20116
More information about the llvm-commits
mailing list