Internals - the Optimizer
The Solidity optimizer operates on assembly, thus it may be and is also utilized by alternative languages. It splits the sequence of directions into basic blocks at points that square measure arduous to maneuver.
These are basically all instructions that modify change the control flow (
calls, etc), instructions that have side effects apart from
EXTCODECOPY, but also
CALLDATALOAD and others).
Inside of such a block, the directions square measure analysed and each modification to the stack, to memory or storage is recorded as associate expression that consists of associate instruction and an inventory of arguments that square measure basically tips to alternative expressions.
The main plan is currently to search out expressions that square measure forever equal (or each input) and mix them into associate expression category. The optimizer 1st tries to search out every new expression in an exceedingly list of already notable expressions.
If this doesn't work, the expression is simplified per rules like
constant + constant = sum_of_constants or X * 1 = X.
Since this is often done recursively, we will conjointly apply the latter rule if the second issue could be a additional advanced expression wherever we all know that it'll forever value to 1.
Modifications to storage and memory locations got to erase data concerning storage and memory locations that aren't notable to show a discrepancy.
If we have a tendency to 1st write to location x so to location y and each square measure input variables, the second may write the primary, therefore we have a tendency to really don't grasp what's hold on at x once we have a tendency to wrote to y.
On the opposite hand, if a simplification of the expression x - y evaluates to a non-zero constant, we all know that we will keep our data concerning what's hold on at x.
At the tip of this method, we all know that expressions got to air the stack within the finish and have list of modifications to memory and storage. From these expressions that are literally required, a dependency graph is made and each operation that's not a part of this graph is basically born.
Now new code is generated that applies the modifications to memory and storage within the order they were created within the original code (dropping modifications that were found to not be needed) and at last, generates all values that square measure needed to air the stack within the correct place.
These steps square measure applied to every basic block and also the recently generated code is employed as replacement if it's smaller.
If a basic block is split at a
JUMPI and through the analysis, the condition evaluates to a relentless, the
JUMPI is replaced counting on the worth of the constant, and so code like
var x = 7; data = 9; if (data[x] != x + 2) return 2; else,return 1; // is simplified to code which can also be compiled from data = 9; return 1; // even though the instructions contained a jump in the beginning.