pass3.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456
  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 3:
  22. * - optimize $i = $i+expr to $i+=expr
  23. * - optimize series of JMPs
  24. * - change $i++ to ++$i where possible
  25. */
  26. #include "php.h"
  27. #include "Optimizer/zend_optimizer.h"
  28. #include "Optimizer/zend_optimizer_internal.h"
  29. #include "zend_API.h"
  30. #include "zend_constants.h"
  31. #include "zend_execute.h"
  32. #include "zend_vm.h"
  33. /* we use "jmp_hitlist" to avoid infinity loops during jmp optimization */
  34. #define CHECK_JMP(target, label) \
  35. for (i=0; i<jmp_hitlist_count; i++) { \
  36. if (jmp_hitlist[i] == ZEND_OP1_JMP_ADDR(target)) { \
  37. goto label; \
  38. } \
  39. } \
  40. jmp_hitlist[jmp_hitlist_count++] = ZEND_OP1_JMP_ADDR(target);
  41. #define CHECK_JMP2(target, label) \
  42. for (i=0; i<jmp_hitlist_count; i++) { \
  43. if (jmp_hitlist[i] == ZEND_OP2_JMP_ADDR(target)) { \
  44. goto label; \
  45. } \
  46. } \
  47. jmp_hitlist[jmp_hitlist_count++] = ZEND_OP2_JMP_ADDR(target);
  48. void zend_optimizer_pass3(zend_op_array *op_array, zend_optimizer_ctx *ctx)
  49. {
  50. zend_op *opline;
  51. zend_op *end = op_array->opcodes + op_array->last;
  52. zend_op **jmp_hitlist;
  53. int jmp_hitlist_count;
  54. int i;
  55. uint32_t opline_num = 0;
  56. ALLOCA_FLAG(use_heap);
  57. jmp_hitlist = (zend_op**)do_alloca(sizeof(zend_op*)*op_array->last, use_heap);
  58. opline = op_array->opcodes;
  59. while (opline < end) {
  60. jmp_hitlist_count = 0;
  61. switch (opline->opcode) {
  62. case ZEND_ADD:
  63. case ZEND_SUB:
  64. case ZEND_MUL:
  65. case ZEND_DIV:
  66. case ZEND_MOD:
  67. case ZEND_POW:
  68. case ZEND_CONCAT:
  69. case ZEND_SL:
  70. case ZEND_SR:
  71. case ZEND_BW_OR:
  72. case ZEND_BW_AND:
  73. case ZEND_BW_XOR:
  74. {
  75. zend_op *next_opline = opline + 1;
  76. while (next_opline < end && next_opline->opcode == ZEND_NOP) {
  77. ++next_opline;
  78. }
  79. if (next_opline >= end || next_opline->opcode != ZEND_ASSIGN) {
  80. break;
  81. }
  82. /* change $i=expr+$i to $i=$i+expr so that the following optimization
  83. * works on it. Only do this if we are ignoring operator overloading,
  84. * as operand order might be significant otherwise. */
  85. if ((ctx->optimization_level & ZEND_OPTIMIZER_IGNORE_OVERLOADING)
  86. && (opline->op2_type & (IS_VAR | IS_CV))
  87. && opline->op2.var == next_opline->op1.var &&
  88. (opline->opcode == ZEND_ADD ||
  89. opline->opcode == ZEND_MUL ||
  90. opline->opcode == ZEND_BW_OR ||
  91. opline->opcode == ZEND_BW_AND ||
  92. opline->opcode == ZEND_BW_XOR)) {
  93. zend_uchar tmp_type = opline->op1_type;
  94. znode_op tmp = opline->op1;
  95. if (opline->opcode != ZEND_ADD
  96. || (opline->op1_type == IS_CONST
  97. && Z_TYPE(ZEND_OP1_LITERAL(opline)) != IS_ARRAY)) {
  98. /* protection from array add: $a = array + $a is not commutative! */
  99. COPY_NODE(opline->op1, opline->op2);
  100. COPY_NODE(opline->op2, tmp);
  101. }
  102. }
  103. if ((opline->op1_type & (IS_VAR | IS_CV))
  104. && opline->op1.var == next_opline->op1.var
  105. && opline->op1_type == next_opline->op1_type) {
  106. switch (opline->opcode) {
  107. case ZEND_ADD:
  108. opline->opcode = ZEND_ASSIGN_ADD;
  109. break;
  110. case ZEND_SUB:
  111. opline->opcode = ZEND_ASSIGN_SUB;
  112. break;
  113. case ZEND_MUL:
  114. opline->opcode = ZEND_ASSIGN_MUL;
  115. break;
  116. case ZEND_DIV:
  117. opline->opcode = ZEND_ASSIGN_DIV;
  118. break;
  119. case ZEND_MOD:
  120. opline->opcode = ZEND_ASSIGN_MOD;
  121. break;
  122. case ZEND_POW:
  123. opline->opcode = ZEND_ASSIGN_POW;
  124. break;
  125. case ZEND_CONCAT:
  126. opline->opcode = ZEND_ASSIGN_CONCAT;
  127. break;
  128. case ZEND_SL:
  129. opline->opcode = ZEND_ASSIGN_SL;
  130. break;
  131. case ZEND_SR:
  132. opline->opcode = ZEND_ASSIGN_SR;
  133. break;
  134. case ZEND_BW_OR:
  135. opline->opcode = ZEND_ASSIGN_BW_OR;
  136. break;
  137. case ZEND_BW_AND:
  138. opline->opcode = ZEND_ASSIGN_BW_AND;
  139. break;
  140. case ZEND_BW_XOR:
  141. opline->opcode = ZEND_ASSIGN_BW_XOR;
  142. break;
  143. }
  144. COPY_NODE(opline->result, next_opline->result);
  145. MAKE_NOP(next_opline);
  146. opline++;
  147. opline_num++;
  148. }
  149. }
  150. break;
  151. case ZEND_JMP:
  152. if (op_array->fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK) {
  153. break;
  154. }
  155. /* convert L: JMP L+1 to NOP */
  156. if (ZEND_OP1_JMP_ADDR(opline) == opline + 1) {
  157. MAKE_NOP(opline);
  158. goto done_jmp_optimization;
  159. }
  160. /* convert JMP L1 ... L1: JMP L2 to JMP L2 .. L1: JMP L2 */
  161. while (ZEND_OP1_JMP_ADDR(opline) < end
  162. && ZEND_OP1_JMP_ADDR(opline)->opcode == ZEND_JMP) {
  163. zend_op *target = ZEND_OP1_JMP_ADDR(opline);
  164. CHECK_JMP(target, done_jmp_optimization);
  165. ZEND_SET_OP_JMP_ADDR(opline, opline->op1, ZEND_OP1_JMP_ADDR(target));
  166. }
  167. break;
  168. case ZEND_JMP_SET:
  169. case ZEND_COALESCE:
  170. if (op_array->fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK) {
  171. break;
  172. }
  173. while (ZEND_OP2_JMP_ADDR(opline) < end) {
  174. zend_op *target = ZEND_OP2_JMP_ADDR(opline);
  175. if (target->opcode == ZEND_JMP) {
  176. ZEND_SET_OP_JMP_ADDR(opline, opline->op2, ZEND_OP1_JMP_ADDR(target));
  177. } else {
  178. break;
  179. }
  180. }
  181. break;
  182. case ZEND_JMPZ:
  183. case ZEND_JMPNZ:
  184. if (op_array->fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK) {
  185. break;
  186. }
  187. while (ZEND_OP2_JMP_ADDR(opline) < end) {
  188. zend_op *target = ZEND_OP2_JMP_ADDR(opline);
  189. if (target->opcode == ZEND_JMP) {
  190. /* plain JMP */
  191. /* JMPZ(X,L1), L1: JMP(L2) => JMPZ(X,L2), L1: JMP(L2) */
  192. CHECK_JMP(target, done_jmp_optimization);
  193. ZEND_SET_OP_JMP_ADDR(opline, opline->op2, ZEND_OP1_JMP_ADDR(target));
  194. } else if (target->opcode == opline->opcode &&
  195. SAME_VAR(opline->op1, target->op1)) {
  196. /* same opcode and same var as this opcode */
  197. /* JMPZ(X,L1), L1: JMPZ(X,L2) => JMPZ(X,L2), L1: JMPZ(X,L2) */
  198. CHECK_JMP2(target, done_jmp_optimization);
  199. ZEND_SET_OP_JMP_ADDR(opline, opline->op2, ZEND_OP2_JMP_ADDR(target));
  200. } else if (target->opcode == opline->opcode + 3 &&
  201. SAME_VAR(opline->op1, target->op1)) {
  202. /* convert JMPZ(X,L1), L1: T JMPZ_EX(X,L2) to
  203. T = JMPZ_EX(X, L2) */
  204. ZEND_SET_OP_JMP_ADDR(opline, opline->op2, ZEND_OP2_JMP_ADDR(target));
  205. opline->opcode += 3;
  206. COPY_NODE(opline->result, target->result);
  207. break;
  208. } else if (target->opcode == INV_COND(opline->opcode) &&
  209. SAME_VAR(opline->op1, target->op1)) {
  210. /* convert JMPZ(X,L1), L1: JMPNZ(X,L2) to
  211. JMPZ(X,L1+1) */
  212. ZEND_SET_OP_JMP_ADDR(opline, opline->op2, target + 1);
  213. break;
  214. } else if (target->opcode == INV_COND_EX(opline->opcode) &&
  215. SAME_VAR(opline->op1, target->op1)) {
  216. /* convert JMPZ(X,L1), L1: T = JMPNZ_EX(X,L2) to
  217. T = JMPZ_EX(X,L1+1) */
  218. ZEND_SET_OP_JMP_ADDR(opline, opline->op2, target + 1);
  219. opline->opcode += 3;
  220. COPY_NODE(opline->result, target->result);
  221. break;
  222. } else {
  223. break;
  224. }
  225. }
  226. break;
  227. case ZEND_JMPZ_EX:
  228. case ZEND_JMPNZ_EX: {
  229. zend_uchar T_type = opline->result_type;
  230. znode_op T = opline->result;
  231. if (op_array->fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK) {
  232. break;
  233. }
  234. /* convert L: T = JMPZ_EX X,L+1 to T = BOOL(X) */
  235. /* convert L: T = JMPZ_EX T,L+1 to NOP */
  236. if (ZEND_OP2_JMP_ADDR(opline) == opline + 1) {
  237. if (opline->op1.var == opline->result.var) {
  238. MAKE_NOP(opline);
  239. } else {
  240. opline->opcode = ZEND_BOOL;
  241. SET_UNUSED(opline->op2);
  242. }
  243. goto done_jmp_optimization;
  244. }
  245. while (ZEND_OP2_JMP_ADDR(opline) < end) {
  246. zend_op *target = ZEND_OP2_JMP_ADDR(opline);
  247. if (target->opcode == opline->opcode-3 &&
  248. SAME_VAR(target->op1, T)) {
  249. /* convert T=JMPZ_EX(X,L1), L1: JMPZ(T,L2) to
  250. JMPZ_EX(X,L2) */
  251. CHECK_JMP2(target, continue_jmp_ex_optimization);
  252. ZEND_SET_OP_JMP_ADDR(opline, opline->op2, ZEND_OP2_JMP_ADDR(target));
  253. } else if (target->opcode == opline->opcode &&
  254. SAME_VAR(target->op1, T) &&
  255. SAME_VAR(target->result, T)) {
  256. /* convert T=JMPZ_EX(X,L1), L1: T=JMPZ_EX(T,L2) to
  257. JMPZ_EX(X,L2) */
  258. CHECK_JMP2(target, continue_jmp_ex_optimization);
  259. ZEND_SET_OP_JMP_ADDR(opline, opline->op2, ZEND_OP2_JMP_ADDR(target));
  260. } else if (target->opcode == ZEND_JMPZNZ &&
  261. SAME_VAR(target->op1, T)) {
  262. /* Check for JMPZNZ with same cond variable */
  263. zend_op *new_target;
  264. CHECK_JMP2(target, continue_jmp_ex_optimization);
  265. if (opline->opcode == ZEND_JMPZ_EX) {
  266. new_target = ZEND_OP2_JMP_ADDR(target);
  267. } else {
  268. /* JMPNZ_EX */
  269. new_target = ZEND_OFFSET_TO_OPLINE(target, target->extended_value);
  270. }
  271. ZEND_SET_OP_JMP_ADDR(opline, opline->op2, new_target);
  272. } else if ((target->opcode == INV_EX_COND_EX(opline->opcode) ||
  273. target->opcode == INV_EX_COND(opline->opcode)) &&
  274. SAME_VAR(opline->op1, target->op1)) {
  275. /* convert JMPZ_EX(X,L1), L1: JMPNZ_EX(X,L2) to
  276. JMPZ_EX(X,L1+1) */
  277. ZEND_SET_OP_JMP_ADDR(opline, opline->op2, target + 1);
  278. break;
  279. } else if (target->opcode == INV_EX_COND(opline->opcode) &&
  280. SAME_VAR(target->op1, T)) {
  281. /* convert T=JMPZ_EX(X,L1), L1: JMPNZ(T,L2) to
  282. JMPZ_EX(X,L1+1) */
  283. ZEND_SET_OP_JMP_ADDR(opline, opline->op2, target + 1);
  284. break;
  285. } else if (target->opcode == INV_EX_COND_EX(opline->opcode) &&
  286. SAME_VAR(target->op1, T) &&
  287. SAME_VAR(target->result, T)) {
  288. /* convert T=JMPZ_EX(X,L1), L1: T=JMPNZ_EX(T,L2) to
  289. JMPZ_EX(X,L1+1) */
  290. ZEND_SET_OP_JMP_ADDR(opline, opline->op2, target + 1);
  291. break;
  292. } else if (target->opcode == ZEND_BOOL &&
  293. SAME_VAR(opline->result, target->op1)) {
  294. /* convert Y = JMPZ_EX(X,L1), L1: Z = BOOL(Y) to
  295. Z = JMPZ_EX(X,L1+1) */
  296. opline->result.var = target->result.var;
  297. ZEND_SET_OP_JMP_ADDR(opline, opline->op2, target + 1);
  298. break;
  299. } else {
  300. break;
  301. }
  302. } /* while */
  303. continue_jmp_ex_optimization:
  304. break;
  305. #if 0
  306. /* If Ti = JMPZ_EX(X, L) and Ti is not used, convert to JMPZ(X, L) */
  307. {
  308. zend_op *op;
  309. for(op = opline+1; op<end; op++) {
  310. if(op->result_type == IS_TMP_VAR &&
  311. op->result.var == opline->result.var) {
  312. break; /* can pass to part 2 */
  313. }
  314. if(op->opcode == ZEND_JMP ||
  315. op->opcode == ZEND_JMPZ ||
  316. op->opcode == ZEND_JMPZ_EX ||
  317. op->opcode == ZEND_JMPNZ ||
  318. op->opcode == ZEND_JMPNZ_EX ||
  319. op->opcode == ZEND_JMPZNZ ||
  320. op->opcode == ZEND_CASE ||
  321. op->opcode == ZEND_RETURN ||
  322. op->opcode == ZEND_RETURN_BY_REF ||
  323. op->opcode == ZEND_FAST_RET ||
  324. op->opcode == ZEND_FE_FETCH_R ||
  325. op->opcode == ZEND_FE_FETCH_RW ||
  326. op->opcode == ZEND_EXIT) {
  327. break;
  328. }
  329. if(op->op1_type == IS_TMP_VAR &&
  330. op->op1.var == opline->result.var) {
  331. goto done_jmp_optimization;
  332. }
  333. if(op->op2_type == IS_TMP_VAR &&
  334. op->op2.var == opline->result.var) {
  335. goto done_jmp_optimization;
  336. }
  337. } /* for */
  338. for(op = &op_array->opcodes[opline->op2.opline_num]; op<end; op++) {
  339. if(op->result_type == IS_TMP_VAR &&
  340. op->result.var == opline->result.var) {
  341. break; /* can pass to optimization */
  342. }
  343. if(op->opcode == ZEND_JMP ||
  344. op->opcode == ZEND_JMPZ ||
  345. op->opcode == ZEND_JMPZ_EX ||
  346. op->opcode == ZEND_JMPNZ ||
  347. op->opcode == ZEND_JMPNZ_EX ||
  348. op->opcode == ZEND_JMPZNZ ||
  349. op->opcode == ZEND_CASE ||
  350. op->opcode == ZEND_RETURN ||
  351. op->opcode == ZEND_RETURN_BY_REF ||
  352. op->opcode == ZEND_FAST_RET ||
  353. op->opcode == ZEND_FE_FETCH_R ||
  354. op->opcode == ZEND_FE_FETCH_RW ||
  355. op->opcode == ZEND_EXIT) {
  356. break;
  357. }
  358. if(op->op1_type == IS_TMP_VAR &&
  359. op->op1.var == opline->result.var) {
  360. goto done_jmp_optimization;
  361. }
  362. if(op->op2_type == IS_TMP_VAR &&
  363. op->op2.var == opline->result.var) {
  364. goto done_jmp_optimization;
  365. }
  366. }
  367. opline->opcode = opline->opcode-3; /* JMP_EX -> JMP */
  368. SET_UNUSED(opline->result);
  369. break;
  370. }
  371. #endif
  372. }
  373. break;
  374. case ZEND_JMPZNZ:
  375. if (op_array->fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK) {
  376. break;
  377. }
  378. /* JMPZNZ(X,L1,L2), L1: JMP(L3) => JMPZNZ(X,L3,L2), L1: JMP(L3) */
  379. while (ZEND_OP2_JMP_ADDR(opline) < end
  380. && ZEND_OP2_JMP_ADDR(opline)->opcode == ZEND_JMP) {
  381. zend_op *target = ZEND_OP2_JMP_ADDR(opline);
  382. CHECK_JMP(target, continue_jmpznz_optimization);
  383. ZEND_SET_OP_JMP_ADDR(opline, opline->op2, ZEND_OP1_JMP_ADDR(target));
  384. }
  385. continue_jmpznz_optimization:
  386. /* JMPZNZ(X,L1,L2), L2: JMP(L3) => JMPZNZ(X,L1,L3), L2: JMP(L3) */
  387. while (ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value) < end
  388. && ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value)->opcode == ZEND_JMP) {
  389. zend_op *target = ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value);
  390. CHECK_JMP(target, done_jmp_optimization);
  391. opline->extended_value = ZEND_OPLINE_TO_OFFSET(opline, ZEND_OP1_JMP_ADDR(target));
  392. }
  393. break;
  394. case ZEND_POST_INC_OBJ:
  395. case ZEND_POST_DEC_OBJ:
  396. case ZEND_POST_INC:
  397. case ZEND_POST_DEC: {
  398. /* POST_INC, FREE => PRE_INC */
  399. zend_op *next_op = opline + 1;
  400. if (next_op >= end) {
  401. break;
  402. }
  403. if (next_op->opcode == ZEND_FREE &&
  404. next_op->op1.var == opline->result.var) {
  405. MAKE_NOP(next_op);
  406. opline->opcode -= 2;
  407. opline->result_type = IS_UNUSED;
  408. }
  409. }
  410. break;
  411. }
  412. done_jmp_optimization:
  413. opline++;
  414. opline_num++;
  415. }
  416. free_alloca(jmp_hitlist, use_heap);
  417. }