[PATCH] D65718: [LangRef] Document forward-progress requirement

Sassa Nf via Phabricator via llvm-commits llvm-commits at lists.llvm.org
Sun Aug 9 03:24:43 PDT 2020


sassa.nf added a comment.

I am going to add that the optimization is on the same lines as the more common case:

  fn some_fn(y) {
     x = y % 42;
     if y < 0 return 0;
  ...
  }

Modern CPUs will attempt to divide, compare and prefetch the code on the predicted success branch in parallel, as and when the corresponding processing units become available. That is, it is possible that for negative `y` the return of `0` will be observed by the caller before the division is completed. The simple consequence is that the only way you can detect that the division has not started, is to write the code that will test the division outcome.

Since you can't write the code that expects the division to have finished without checking the division outcome, you can't also write the code that expects the division to have started. This means the compiler is free to reorder the computation:

  fn some_fn(y) {
     if y < 0 return 0;
     x = y % 42;
  ...
  }

The loop is no different: if the return value of the function does not depend on the result of the loop, it means it is not really waiting for the loop to complete, and cannot detect whether the loop has started.
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

This is not to say what LLVM compiler, Rust or other languages should do. This is only to say that there are other reasons than forward progress guarantee that can make this optimization happen.

In fact, there are other languages without forward progress guarantee, but which do speak about the observability of effects, and where this sort of optimization is possible, and in fact is happening. The statement about observability of the effect is the necessary condition. The solution in those languages is to make the entering of the loop observable, so one may observe when the loop has not been entered.

Example in Rust:

  fn is_odd(x: mut u32, y: &mut u32) -> bool {
     while x != 0 {
        x += 2;
     }
     *y = x;
     return false
  }

Now `x` escapes as the witness that the loop has finished. Passing some global reference `bottom_has_been_computed` with volatile semantics as `y` will ensure the infinite loop cannot be optimized away - because there are observable effects of not having it executed.


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D65718/new/

https://reviews.llvm.org/D65718



More information about the llvm-commits mailing list