2

In Speculative Optimizations for WebAssembly using Deopts and Inlining the following statement is made:

The difference is in the code for the slow path. Without deopts, we don’t have the option of returning to baseline code, so the optimized code must handle the full, generic behavior of an indirect call, as shown in the yellow box on the left. This unfortunately impedes further optimizations and thus results in slower execution.

This implies that with deopts further optimizations can be applied, such as dead code elimination. I wonder how the Deoptimizer is able to reconstruct values that were eliminated from the inlined call, but are required for the generic call.

Consider the following example in C++ (which could then be compiled to WASM):

// inlined target
int pick(int a, int b) {
  return a;
}

// call site
int(int, int)* virt_pick = pick;

int a = 0, b = 0;
for (int i = 0; i < 100; i++) {
   a += rand();
   b += a % 10;
}

int result = (*virt_pick)(a, b);

If pick is inlined, this could potentially be optimized to something like the following, where b is fully eliminated:

int a = 0;
for (int i = 0; i < 100; i++) {
   a += rand();
}

int result = a;

But once deoptimization happens, i.e. by replacing virt_pick with a function that accesses b, b is nowhere on the stack / in registers.

How does this work? Does the Deoptimizer compute values on demand or are values still computed that might be needed for deoptimization?

1 Answer 1

3

The latter: values that might be needed on deoptimization must still be computed and kept around. Any other strategy would be too complicated in the general case (values could depend on things that happened before in ways that are impossible to reconstruct after the fact). This is one reason why speculation and inlining doesn't always enable the same degree of optimization that could be achieved by hand, but in practice it's often not worth worrying about it.

Sign up to request clarification or add additional context in comments.

5 Comments

Thanks for clarifying! So in a nutshell, this form of inlining avoids the virtual call and might simplify „inwards“, but it does not simplify the call site. Yeah I also do not „worry about it in practice“, this was rather an academic question :)
Inlining may well simplify the call site, just not in the particular case constructed in your example (when the call site prepares a value that the inlined callee doesn't use). But, for example, when no calls with arbitrary side effects are left after the inlining phase, that often helps the optimizations that can be applied to a loop.
„ when no calls with arbitrary side effects are left after the inlining phase“ … so if no deoptimization can happen?
(seems I can't create a nested reply) No, that's mostly unrelated to deopts. Calling some other function in general must be assumed to have arbitrary side effects, so e.g. values must be re-read from memory afterwards. But if all calls got inlined (possibly with deopt points, if speculation was involved), the compiler can see better which values may have been modified, requiring reload, and which can be cached through several loop iterations because nothing can possibly modify them.
Ah right, I was thinking about deopt as just being „a call with arbitrary side-effects“ but it is (obviously) not - as we deoptimize we anyways undo any speculation, so the deopt does not constrain anything, except that we need a way to reach the deoptimized state. And right, inlining adds visibility to side-effects so there might be optimizations (apart from eliminating dead arguments) that could be applied to the outside … Thanks, my mental model is getting more clear on this :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.