12 comments

[ 3.8 ms ] story [ 32.6 ms ] thread
Good call. I checked river sand-bar width in the old code. It turns-out that it offset by random x and y. Currently, I have just x offset. The old code with y sandbar offset caused bigger bars.

It's good, now. Thanks for taking interest. It's true I was negligent not investigating.

Fascinating stuff! Reminds me very much of swift's guard statement.
Yes they're definitely close, but I don't know if there are subtle differences in semantics between what we have in LLVM and what Swift needs (since I'm not familiar with Swift).
> Now if arr.length is 1 then we’ll deoptimize to the interpreter with false as the value of 0 u< arr.length

How so? Why does the test change?

The optimizer recognizes that the variable `condition` and the condition with the guard's if-statement are the same (edit: inverses actually), so within the if-statement's body, it can assume that `condition` is false. But when other guard cases are merged in, that's no longer the case.

In other words, the general optimization is to convert

    bool A = condition(); 
    if (!A) { f(A); }
to:

    bool A = condition();
    if (!A) { f(false); }
i.e., that we can assume an if-condition is true within the body of that if-statement. The problem is that the check-widening reuses the body of the if-statement: the code

    bool A = condition();
    if (!A) { f(A); }
    bool B = condition();
    if (!B) { f(A); }
is converted by check-widening to the first snippet above, which then becomes the second. Sanjoy's observation is that guard-widening is almost correct (we still want to pursue this route) because `f` (the deoptimization escape) is really what we want to call in both cases, but we just need to get the value of `A` right by somehow letting the optimizer know that we're reusing the if-body and that it can't assume anything about the condition that got us there.

Also, this is really really clever and I enjoyed the post, OP!

Why "discard the current runtime frame, and resume execution in the interpreter" rather than jump into a compiled but unoptimized version of the function?
You could do the latter too, and that's what V8 does for example (at least until they roll out the interpreter).
Yup. The "obvious" downside to that is that you'll burn CPU cycles and memory by generating and keeping around a slow-but-correct compile for every function.
(comment deleted)