[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