Between compiling the op tree and executing it, Perl goes through three stages of optimization.
The first stage actually happens as the tree is being constructed. Once Perl creates an op, it passes it off to a check routine. We saw above how the check routines are assigned to operators in the op table; an index op will be passed to ck_index. This routine may manipulate the op in any way it pleases, including freeing it, replacing it with a different op, or adding new ops above or below it. They are sometimes called in a chain: for instance, the check routine for index simply tests to see if the string being sought is a constant, and if so, performs a Fast Boyer-Moore string compilation to speed up the search at runtime; then it calls the general function-checking routine ck_fun.
Secondly, the constant folding routine fold_constants is called if appropriate. This tests to see whether all of the descendents of the op are constants, and if they are, runs the operator as if it was a little program, collects the result and replaces the op with a constant op reflecting that result. You can tell if constants have been folded by using the "deparse" compiler backend (see Section 7.2.3):
% perl -MO=Deparse -e 'print (3+5+8+$foo)' print 16 + $foo;Here, the 3+5 has been constant-folded into 8, and then 8+8 is constant-folded to 16.
Finally, the peephole optimizer peep is called. This examines each op in the tree in execution order, and attempts to determine "local" optimizations by "thinking ahead" one or two ops and seeing if multiple operations can be combined into one. It also checks for lexical issues such as the effect of use strict on bareword constants.