pass2.c 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. /*
  2. +----------------------------------------------------------------------+
  3. | Zend OPcache |
  4. +----------------------------------------------------------------------+
  5. | Copyright (c) 1998-2018 The PHP Group |
  6. +----------------------------------------------------------------------+
  7. | This source file is subject to version 3.01 of the PHP license, |
  8. | that is bundled with this package in the file LICENSE, and is |
  9. | available through the world-wide-web at the following url: |
  10. | http://www.php.net/license/3_01.txt |
  11. | If you did not receive a copy of the PHP license and are unable to |
  12. | obtain it through the world-wide-web, please send a note to |
  13. | license@php.net so we can mail you a copy immediately. |
  14. +----------------------------------------------------------------------+
  15. | Authors: Andi Gutmans <andi@php.net> |
  16. | Zeev Suraski <zeev@php.net> |
  17. | Stanislav Malyshev <stas@zend.com> |
  18. | Dmitry Stogov <dmitry@php.net> |
  19. +----------------------------------------------------------------------+
  20. */
  21. /* pass 2:
  22. * - convert non-numeric constants to numeric constants in numeric operators
  23. * - optimize constant conditional JMPs
  24. */
  25. #include "php.h"
  26. #include "Optimizer/zend_optimizer.h"
  27. #include "Optimizer/zend_optimizer_internal.h"
  28. #include "zend_API.h"
  29. #include "zend_constants.h"
  30. #include "zend_execute.h"
  31. #include "zend_vm.h"
  32. void zend_optimizer_pass2(zend_op_array *op_array)
  33. {
  34. zend_op *opline;
  35. zend_op *end = op_array->opcodes + op_array->last;
  36. opline = op_array->opcodes;
  37. while (opline < end) {
  38. switch (opline->opcode) {
  39. case ZEND_ADD:
  40. case ZEND_SUB:
  41. case ZEND_MUL:
  42. case ZEND_DIV:
  43. case ZEND_POW:
  44. if (opline->op1_type == IS_CONST) {
  45. if (Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_STRING) {
  46. /* don't optimise if it should produce a runtime numeric string error */
  47. if (is_numeric_string(Z_STRVAL(ZEND_OP1_LITERAL(opline)), Z_STRLEN(ZEND_OP1_LITERAL(opline)), NULL, NULL, 0)) {
  48. convert_scalar_to_number(&ZEND_OP1_LITERAL(opline));
  49. }
  50. }
  51. }
  52. /* break missing *intentionally* - the assign_op's may only optimize op2 */
  53. case ZEND_ASSIGN_ADD:
  54. case ZEND_ASSIGN_SUB:
  55. case ZEND_ASSIGN_MUL:
  56. case ZEND_ASSIGN_DIV:
  57. case ZEND_ASSIGN_POW:
  58. if (opline->extended_value != 0) {
  59. /* object tristate op - don't attempt to optimize it! */
  60. break;
  61. }
  62. if (opline->op2_type == IS_CONST) {
  63. if (Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_STRING) {
  64. /* don't optimise if it should produce a runtime numeric string error */
  65. if (is_numeric_string(Z_STRVAL(ZEND_OP2_LITERAL(opline)), Z_STRLEN(ZEND_OP2_LITERAL(opline)), NULL, NULL, 0)) {
  66. convert_scalar_to_number(&ZEND_OP2_LITERAL(opline));
  67. }
  68. }
  69. }
  70. break;
  71. case ZEND_MOD:
  72. case ZEND_SL:
  73. case ZEND_SR:
  74. if (opline->op1_type == IS_CONST) {
  75. if (Z_TYPE(ZEND_OP1_LITERAL(opline)) != IS_LONG) {
  76. /* don't optimise if it should produce a runtime numeric string error */
  77. if (!(Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_STRING
  78. && !is_numeric_string(Z_STRVAL(ZEND_OP1_LITERAL(opline)), Z_STRLEN(ZEND_OP1_LITERAL(opline)), NULL, NULL, 0))) {
  79. convert_to_long(&ZEND_OP1_LITERAL(opline));
  80. }
  81. }
  82. }
  83. /* break missing *intentionally - the assign_op's may only optimize op2 */
  84. case ZEND_ASSIGN_MOD:
  85. case ZEND_ASSIGN_SL:
  86. case ZEND_ASSIGN_SR:
  87. if (opline->extended_value != 0) {
  88. /* object tristate op - don't attempt to optimize it! */
  89. break;
  90. }
  91. if (opline->op2_type == IS_CONST) {
  92. if (Z_TYPE(ZEND_OP2_LITERAL(opline)) != IS_LONG) {
  93. /* don't optimise if it should produce a runtime numeric string error */
  94. if (!(Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_STRING
  95. && !is_numeric_string(Z_STRVAL(ZEND_OP2_LITERAL(opline)), Z_STRLEN(ZEND_OP2_LITERAL(opline)), NULL, NULL, 0))) {
  96. convert_to_long(&ZEND_OP2_LITERAL(opline));
  97. }
  98. }
  99. }
  100. break;
  101. case ZEND_CONCAT:
  102. case ZEND_FAST_CONCAT:
  103. if (opline->op1_type == IS_CONST) {
  104. if (Z_TYPE(ZEND_OP1_LITERAL(opline)) != IS_STRING) {
  105. convert_to_string(&ZEND_OP1_LITERAL(opline));
  106. }
  107. }
  108. /* break missing *intentionally - the assign_op's may only optimize op2 */
  109. case ZEND_ASSIGN_CONCAT:
  110. if (opline->extended_value != 0) {
  111. /* object tristate op - don't attempt to optimize it! */
  112. break;
  113. }
  114. if (opline->op2_type == IS_CONST) {
  115. if (Z_TYPE(ZEND_OP2_LITERAL(opline)) != IS_STRING) {
  116. convert_to_string(&ZEND_OP2_LITERAL(opline));
  117. }
  118. }
  119. break;
  120. case ZEND_JMPZ_EX:
  121. case ZEND_JMPNZ_EX:
  122. /* convert Ti = JMPZ_EX(Ti, L) to JMPZ(Ti, L) */
  123. #if 0
  124. /* Disabled unsafe pattern: in conjunction with
  125. * ZEND_VM_SMART_BRANCH() this may improperly eliminate
  126. * assignment to Ti.
  127. */
  128. if (opline->op1_type == IS_TMP_VAR &&
  129. opline->result_type == IS_TMP_VAR &&
  130. opline->op1.var == opline->result.var) {
  131. opline->opcode -= 3;
  132. SET_UNUSED(opline->result);
  133. } else
  134. #endif
  135. /* convert Ti = JMPZ_EX(C, L) => Ti = QM_ASSIGN(C)
  136. in case we know it wouldn't jump */
  137. if (opline->op1_type == IS_CONST) {
  138. int should_jmp = zend_is_true(&ZEND_OP1_LITERAL(opline));
  139. if (opline->opcode == ZEND_JMPZ_EX) {
  140. should_jmp = !should_jmp;
  141. }
  142. if (!should_jmp) {
  143. opline->opcode = ZEND_QM_ASSIGN;
  144. SET_UNUSED(opline->op2);
  145. }
  146. }
  147. break;
  148. case ZEND_JMPZ:
  149. case ZEND_JMPNZ:
  150. if (opline->op1_type == IS_CONST) {
  151. int should_jmp = zend_is_true(&ZEND_OP1_LITERAL(opline));
  152. if (opline->opcode == ZEND_JMPZ) {
  153. should_jmp = !should_jmp;
  154. }
  155. literal_dtor(&ZEND_OP1_LITERAL(opline));
  156. opline->op1_type = IS_UNUSED;
  157. if (should_jmp) {
  158. opline->opcode = ZEND_JMP;
  159. COPY_NODE(opline->op1, opline->op2);
  160. } else {
  161. MAKE_NOP(opline);
  162. }
  163. break;
  164. }
  165. if ((opline + 1)->opcode == ZEND_JMP) {
  166. /* JMPZ(X, L1), JMP(L2) => JMPZNZ(X, L1, L2) */
  167. /* JMPNZ(X, L1), JMP(L2) => JMPZNZ(X, L2, L1) */
  168. if (ZEND_OP2_JMP_ADDR(opline) == ZEND_OP1_JMP_ADDR(opline + 1)) {
  169. /* JMPZ(X, L1), JMP(L1) => NOP, JMP(L1) */
  170. if (opline->op1_type == IS_CV) {
  171. opline->opcode = ZEND_CHECK_VAR;
  172. opline->op2.num = 0;
  173. } else if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) {
  174. opline->opcode = ZEND_FREE;
  175. opline->op2.num = 0;
  176. } else {
  177. MAKE_NOP(opline);
  178. }
  179. } else {
  180. if (opline->opcode == ZEND_JMPZ) {
  181. opline->extended_value = ZEND_OPLINE_TO_OFFSET(opline, ZEND_OP1_JMP_ADDR(opline + 1));
  182. } else {
  183. opline->extended_value = ZEND_OPLINE_TO_OFFSET(opline, ZEND_OP2_JMP_ADDR(opline));
  184. ZEND_SET_OP_JMP_ADDR(opline, opline->op2, ZEND_OP1_JMP_ADDR(opline + 1));
  185. }
  186. opline->opcode = ZEND_JMPZNZ;
  187. }
  188. }
  189. break;
  190. case ZEND_JMPZNZ:
  191. if (opline->op1_type == IS_CONST) {
  192. zend_op *target_opline;
  193. if (zend_is_true(&ZEND_OP1_LITERAL(opline))) {
  194. target_opline = ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value); /* JMPNZ */
  195. } else {
  196. target_opline = ZEND_OP2_JMP_ADDR(opline); /* JMPZ */
  197. }
  198. literal_dtor(&ZEND_OP1_LITERAL(opline));
  199. ZEND_SET_OP_JMP_ADDR(opline, opline->op1, target_opline);
  200. opline->op1_type = IS_UNUSED;
  201. opline->opcode = ZEND_JMP;
  202. }
  203. break;
  204. }
  205. opline++;
  206. }
  207. }