The only optimization included in series 2 was constant folding. Unlike in the previous series, we’re now much better equipped to handle this optimization.
I feel that I should point out that most C compilers do this step, too. I was somewhat reluctant to keep it in at first. However, my reasoning for keeping it is two-fold: one, I think learning a bit about optimizations on an IR is a worthy lesson; and two, it can help make the next step a little quicker.
Once again, the IR comes to the rescue! In the first step of transforming the AST into the IR we created constants. These were objects representing actual values. This crucial step makes it much easier on us now.
Unary and Binary operations are ideal candidates for folding. We have already done the work of verifying types and correctness so we can ignore all that now. When we check if a value is a certain type we can be confident that information is correct.
Looking at the code you can see that it’s pretty simple. Traverse the tree depth first. If both operands of a binary object, or the single operand of a unary object, are values of the correct type we can fold them together. We then return the result as a new constant value and replace the previous object with it.
Moving back up the tree we repeat the process until we exhaust all the foldable values.
You can see the value in converting basic literals into constants when building the initial IR tree. It makes folding constants together much, much simpler.
Onward and upward!