More aggressive methods that are commonly employed are constant propagation, dead code elimination and function inlining. This type of minimization typically takes a long time to perform, makes debugging the optimized code much harder, and is therefore typically only intended to be applied on release builds.
The PreExecuter is an optional feature of Cheerp, and needs to be activated with the
--cheerp-preexecute command line flag. We currently use by default the PreExecuter on all the standard libraries shipped with Cheerp.
Dead code elimination is not sufficient
The Cheerp PreExecuter goes a step further compared to other aggressive minimization methods. It optimizes the output by selectively executing portions of code at compile-time, recording the resulting global memory state, then substituting the code with a global memory initialization using the saved values.
Although constructors cannot be eliminated (since that would change the semantics of the program), they can be evaluated at compile time. The constructors of libstdc++ do not depend on any run time input (e.g. environment variables or command line flags), which allows the compiler to compute the initialized values of the constructors at compile time. The initialized values are then stored in memory and loaded at run time.
Effects of the PreExecuter - Benchmarks
In order to evaluate the effect of the PreExecuter, we will refer to two simple examples:
- An Hello World program.
2. A simple program which uses the standard library’s vector class.
The following plot illustrates the output size of these examples with different levels of optimization: our standard minification available in Cheerp 1.1, a more aggressive baseline minimization included in Cheerp 1.2, and the maximum optimization including the PreExecuter.
Compiling the hello world program with only
push_back(). That method includes a slow path that reallocates the array when the array is full and another element is about to be added. The for-loop is also unrolled 10 times, since the loop body is small, but that increases the file size a bit as well.
A global constructor can be pre-executed a compile time only if it meets certain criteria:
All the code in the constructor needs to be type safe and representable with the Cheerp Memory Model. This is automatically true for all the code that compiles in
genericjsmode, but not necessarily for code compiled to
The constructor cannot call code defined in the
clientnamespace, or use the