PageRenderTime 42ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/ext/opcache/Optimizer/optimize_func_calls.c

https://github.com/php/php-src
C | 309 lines | 249 code | 23 blank | 37 comment | 91 complexity | a2beb9ecd3e6ba29ad8e6d1d7b300d6f MD5 | raw file
Possible License(s): BSD-2-Clause, BSD-3-Clause, MPL-2.0-no-copyleft-exception, LGPL-2.1
  1. /*
  2. +----------------------------------------------------------------------+
  3. | Zend OPcache |
  4. +----------------------------------------------------------------------+
  5. | Copyright (c) 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: Dmitry Stogov <dmitry@php.net> |
  16. | Xinchen Hui <laruence@php.net> |
  17. +----------------------------------------------------------------------+
  18. */
  19. /* pass 4
  20. * - optimize INIT_FCALL_BY_NAME to DO_FCALL
  21. */
  22. #include "php.h"
  23. #include "Optimizer/zend_optimizer.h"
  24. #include "Optimizer/zend_optimizer_internal.h"
  25. #include "zend_API.h"
  26. #include "zend_constants.h"
  27. #include "zend_execute.h"
  28. #include "zend_vm.h"
  29. #define ZEND_OP1_IS_CONST_STRING(opline) \
  30. (opline->op1_type == IS_CONST && \
  31. Z_TYPE(op_array->literals[(opline)->op1.constant]) == IS_STRING)
  32. #define ZEND_OP2_IS_CONST_STRING(opline) \
  33. (opline->op2_type == IS_CONST && \
  34. Z_TYPE(op_array->literals[(opline)->op2.constant]) == IS_STRING)
  35. typedef struct _optimizer_call_info {
  36. zend_function *func;
  37. zend_op *opline;
  38. zend_bool is_prototype;
  39. zend_bool try_inline;
  40. uint32_t func_arg_num;
  41. } optimizer_call_info;
  42. static void zend_delete_call_instructions(zend_op *opline)
  43. {
  44. int call = 0;
  45. while (1) {
  46. switch (opline->opcode) {
  47. case ZEND_INIT_FCALL_BY_NAME:
  48. case ZEND_INIT_NS_FCALL_BY_NAME:
  49. case ZEND_INIT_STATIC_METHOD_CALL:
  50. case ZEND_INIT_METHOD_CALL:
  51. case ZEND_INIT_FCALL:
  52. if (call == 0) {
  53. MAKE_NOP(opline);
  54. return;
  55. }
  56. /* break missing intentionally */
  57. case ZEND_NEW:
  58. case ZEND_INIT_DYNAMIC_CALL:
  59. case ZEND_INIT_USER_CALL:
  60. call--;
  61. break;
  62. case ZEND_DO_FCALL:
  63. case ZEND_DO_ICALL:
  64. case ZEND_DO_UCALL:
  65. case ZEND_DO_FCALL_BY_NAME:
  66. call++;
  67. break;
  68. case ZEND_SEND_VAL:
  69. case ZEND_SEND_VAR:
  70. if (call == 0) {
  71. if (opline->op1_type == IS_CONST) {
  72. MAKE_NOP(opline);
  73. } else if (opline->op1_type == IS_CV) {
  74. opline->opcode = ZEND_CHECK_VAR;
  75. opline->extended_value = 0;
  76. opline->result.var = 0;
  77. } else {
  78. opline->opcode = ZEND_FREE;
  79. opline->extended_value = 0;
  80. opline->result.var = 0;
  81. }
  82. }
  83. break;
  84. }
  85. opline--;
  86. }
  87. }
  88. static void zend_try_inline_call(zend_op_array *op_array, zend_op *fcall, zend_op *opline, zend_function *func)
  89. {
  90. if (func->type == ZEND_USER_FUNCTION
  91. && !(func->op_array.fn_flags & (ZEND_ACC_ABSTRACT|ZEND_ACC_HAS_TYPE_HINTS))
  92. /* TODO: function copied from trait may be inconsistent ??? */
  93. && !(func->op_array.fn_flags & (ZEND_ACC_TRAIT_CLONE))
  94. && fcall->extended_value >= func->op_array.required_num_args
  95. && func->op_array.opcodes[func->op_array.num_args].opcode == ZEND_RETURN) {
  96. zend_op *ret_opline = func->op_array.opcodes + func->op_array.num_args;
  97. if (ret_opline->op1_type == IS_CONST) {
  98. uint32_t i, num_args = func->op_array.num_args;
  99. num_args += (func->op_array.fn_flags & ZEND_ACC_VARIADIC) != 0;
  100. if (fcall->opcode == ZEND_INIT_METHOD_CALL && fcall->op1_type == IS_UNUSED) {
  101. /* TODO: we can't inlne methods, because $this may be used
  102. * not in object context ???
  103. */
  104. return;
  105. }
  106. for (i = 0; i < num_args; i++) {
  107. /* Don't inline functions with by-reference arguments. This would require
  108. * correct handling of INDIRECT arguments. */
  109. if (ZEND_ARG_SEND_MODE(&func->op_array.arg_info[i])) {
  110. return;
  111. }
  112. }
  113. if (fcall->extended_value < func->op_array.num_args) {
  114. /* don't inline functions with named constants in default arguments */
  115. i = fcall->extended_value;
  116. do {
  117. if (Z_TYPE_P(CRT_CONSTANT_EX(&func->op_array, &func->op_array.opcodes[i], func->op_array.opcodes[i].op2)) == IS_CONSTANT_AST) {
  118. return;
  119. }
  120. i++;
  121. } while (i < func->op_array.num_args);
  122. }
  123. if (RETURN_VALUE_USED(opline)) {
  124. zval zv;
  125. ZVAL_COPY(&zv, CRT_CONSTANT_EX(&func->op_array, ret_opline, ret_opline->op1));
  126. opline->opcode = ZEND_QM_ASSIGN;
  127. opline->op1_type = IS_CONST;
  128. opline->op1.constant = zend_optimizer_add_literal(op_array, &zv);
  129. SET_UNUSED(opline->op2);
  130. } else {
  131. MAKE_NOP(opline);
  132. }
  133. zend_delete_call_instructions(opline-1);
  134. }
  135. }
  136. }
  137. void zend_optimize_func_calls(zend_op_array *op_array, zend_optimizer_ctx *ctx)
  138. {
  139. zend_op *opline = op_array->opcodes;
  140. zend_op *end = opline + op_array->last;
  141. int call = 0;
  142. void *checkpoint;
  143. optimizer_call_info *call_stack;
  144. if (op_array->last < 2) {
  145. return;
  146. }
  147. checkpoint = zend_arena_checkpoint(ctx->arena);
  148. call_stack = zend_arena_calloc(&ctx->arena, op_array->last / 2, sizeof(optimizer_call_info));
  149. while (opline < end) {
  150. switch (opline->opcode) {
  151. case ZEND_INIT_FCALL_BY_NAME:
  152. case ZEND_INIT_NS_FCALL_BY_NAME:
  153. case ZEND_INIT_STATIC_METHOD_CALL:
  154. case ZEND_INIT_METHOD_CALL:
  155. case ZEND_INIT_FCALL:
  156. case ZEND_NEW:
  157. /* The argument passing optimizations are valid for prototypes as well,
  158. * as inheritance cannot change between ref <-> non-ref arguments. */
  159. call_stack[call].func = zend_optimizer_get_called_func(
  160. ctx->script, op_array, opline, &call_stack[call].is_prototype);
  161. call_stack[call].try_inline =
  162. !call_stack[call].is_prototype && opline->opcode != ZEND_NEW;
  163. /* break missing intentionally */
  164. case ZEND_INIT_DYNAMIC_CALL:
  165. case ZEND_INIT_USER_CALL:
  166. call_stack[call].opline = opline;
  167. call_stack[call].func_arg_num = (uint32_t)-1;
  168. call++;
  169. break;
  170. case ZEND_DO_FCALL:
  171. case ZEND_DO_ICALL:
  172. case ZEND_DO_UCALL:
  173. case ZEND_DO_FCALL_BY_NAME:
  174. call--;
  175. if (call_stack[call].func && call_stack[call].opline) {
  176. zend_op *fcall = call_stack[call].opline;
  177. if (fcall->opcode == ZEND_INIT_FCALL) {
  178. /* nothing to do */
  179. } else if (fcall->opcode == ZEND_INIT_FCALL_BY_NAME) {
  180. fcall->opcode = ZEND_INIT_FCALL;
  181. fcall->op1.num = zend_vm_calc_used_stack(fcall->extended_value, call_stack[call].func);
  182. literal_dtor(&ZEND_OP2_LITERAL(fcall));
  183. fcall->op2.constant = fcall->op2.constant + 1;
  184. opline->opcode = zend_get_call_op(fcall, call_stack[call].func);
  185. } else if (fcall->opcode == ZEND_INIT_NS_FCALL_BY_NAME) {
  186. fcall->opcode = ZEND_INIT_FCALL;
  187. fcall->op1.num = zend_vm_calc_used_stack(fcall->extended_value, call_stack[call].func);
  188. literal_dtor(&op_array->literals[fcall->op2.constant]);
  189. literal_dtor(&op_array->literals[fcall->op2.constant + 2]);
  190. fcall->op2.constant = fcall->op2.constant + 1;
  191. opline->opcode = zend_get_call_op(fcall, call_stack[call].func);
  192. } else if (fcall->opcode == ZEND_INIT_STATIC_METHOD_CALL
  193. || fcall->opcode == ZEND_INIT_METHOD_CALL
  194. || fcall->opcode == ZEND_NEW) {
  195. /* We don't have specialized opcodes for this, do nothing */
  196. } else {
  197. ZEND_ASSERT(0);
  198. }
  199. if ((ZEND_OPTIMIZER_PASS_16 & ctx->optimization_level)
  200. && call_stack[call].try_inline) {
  201. zend_try_inline_call(op_array, fcall, opline, call_stack[call].func);
  202. }
  203. }
  204. call_stack[call].func = NULL;
  205. call_stack[call].opline = NULL;
  206. call_stack[call].try_inline = 0;
  207. call_stack[call].func_arg_num = (uint32_t)-1;
  208. break;
  209. case ZEND_FETCH_FUNC_ARG:
  210. case ZEND_FETCH_STATIC_PROP_FUNC_ARG:
  211. case ZEND_FETCH_OBJ_FUNC_ARG:
  212. case ZEND_FETCH_DIM_FUNC_ARG:
  213. if (call_stack[call - 1].func) {
  214. ZEND_ASSERT(call_stack[call - 1].func_arg_num != (uint32_t)-1);
  215. if (ARG_SHOULD_BE_SENT_BY_REF(call_stack[call - 1].func, call_stack[call - 1].func_arg_num)) {
  216. if (opline->opcode != ZEND_FETCH_STATIC_PROP_FUNC_ARG) {
  217. opline->opcode -= 9;
  218. } else {
  219. opline->opcode = ZEND_FETCH_STATIC_PROP_W;
  220. }
  221. } else {
  222. if (opline->opcode == ZEND_FETCH_DIM_FUNC_ARG
  223. && opline->op2_type == IS_UNUSED) {
  224. /* FETCH_DIM_FUNC_ARG supports UNUSED op2, while FETCH_DIM_R does not.
  225. * Performing the replacement would create an invalid opcode. */
  226. call_stack[call - 1].try_inline = 0;
  227. break;
  228. }
  229. if (opline->opcode != ZEND_FETCH_STATIC_PROP_FUNC_ARG) {
  230. opline->opcode -= 12;
  231. } else {
  232. opline->opcode = ZEND_FETCH_STATIC_PROP_R;
  233. }
  234. }
  235. }
  236. break;
  237. case ZEND_SEND_VAL_EX:
  238. if (call_stack[call - 1].func) {
  239. if (ARG_MUST_BE_SENT_BY_REF(call_stack[call - 1].func, opline->op2.num)) {
  240. /* We won't convert it into_DO_FCALL to emit error at run-time */
  241. call_stack[call - 1].opline = NULL;
  242. } else {
  243. opline->opcode = ZEND_SEND_VAL;
  244. }
  245. }
  246. break;
  247. case ZEND_CHECK_FUNC_ARG:
  248. if (call_stack[call - 1].func) {
  249. call_stack[call - 1].func_arg_num = opline->op2.num;
  250. MAKE_NOP(opline);
  251. }
  252. break;
  253. case ZEND_SEND_VAR_EX:
  254. case ZEND_SEND_FUNC_ARG:
  255. if (call_stack[call - 1].func) {
  256. call_stack[call - 1].func_arg_num = (uint32_t)-1;
  257. if (ARG_SHOULD_BE_SENT_BY_REF(call_stack[call - 1].func, opline->op2.num)) {
  258. opline->opcode = ZEND_SEND_REF;
  259. } else {
  260. opline->opcode = ZEND_SEND_VAR;
  261. }
  262. }
  263. break;
  264. case ZEND_SEND_VAR_NO_REF_EX:
  265. if (call_stack[call - 1].func) {
  266. if (ARG_MUST_BE_SENT_BY_REF(call_stack[call - 1].func, opline->op2.num)) {
  267. opline->opcode = ZEND_SEND_VAR_NO_REF;
  268. } else if (ARG_MAY_BE_SENT_BY_REF(call_stack[call - 1].func, opline->op2.num)) {
  269. opline->opcode = ZEND_SEND_VAL;
  270. } else {
  271. opline->opcode = ZEND_SEND_VAR;
  272. }
  273. }
  274. break;
  275. case ZEND_SEND_UNPACK:
  276. case ZEND_SEND_USER:
  277. case ZEND_SEND_ARRAY:
  278. call_stack[call - 1].try_inline = 0;
  279. break;
  280. default:
  281. break;
  282. }
  283. opline++;
  284. }
  285. zend_arena_release(&ctx->arena, checkpoint);
  286. }