webassemblyv8deoptimization

How are eliminated values reconstructed during deoptimization in V8?


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?


Solution

  • 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.