本文基于的PHP8 commit为:14806e0824ecd598df74cac855868422e44aea53
有如下代码:
1 |
|
对应的opcode为:
1 | L3 #0 ECHO 6 |
我们可以分析出,这个echo表达式对应的AST大概如下:
1 | ZEND_ECHO |
所以,可以看到,PHP代码生成的时候,很轻松的进行优化了:
1 | ZEND_ECHO |
最后就会优化为:
1 | ZEND_ECHO |
所以生成的opcode只有一条。
那么,我们再来看一个PHP目前没有优化的例子:
1 |
|
对应的opcode如下:
1 | L3 #0 ASSIGN $x 1 |
可以看到,这里没有进行优化,理论上来说,对常量进行折叠的话,可以减少一条opcode。那么为什么PHP内核它没有对这种情况进行优化呢?我们先来看一看这条语句对应的AST:
1 | ZEND_ECHO |
可以发现,如果对AST进行深度遍历的话,是先进行x + 2,而x是一个变量,折叠不了,所以就没有优化到了,具体的代码是这样的(在函数zend_compile_binary_op里面):
1 | if (left_node.op_type == IS_CONST && right_node.op_type == IS_CONST) { |
我们发现,折叠的情况只有是当左右节点都为IS_CONST类型的时候,才会生效。
那么,面对这种情况,理论上我们可以怎么解决呢?我们可以对这个AST进行旋转,得到:
1 | ZEND_ECHO |
然后,我们就可以优化为:
1 | ZEND_ECHO |
既然,PHP没有做这方面的优化工作,那么,我们写代码的时候,就可以稍微注意一下了。常量尽可能的往左边靠拢,例如1 + 2 + x这样。
后续我们的yaphp会使用LLVM来对这方面进行优化。