pass3.c 14 KB

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