]> granicus.if.org Git - php/commitdiff
Added an optimization pass to convert FCALL_BY_NAME into DO_FCALL.
authorDmitry Stogov <dmitry@zend.com>
Thu, 18 Apr 2013 18:12:31 +0000 (22:12 +0400)
committerDmitry Stogov <dmitry@zend.com>
Thu, 18 Apr 2013 18:12:31 +0000 (22:12 +0400)
ext/opcache/Optimizer/optimize_func_calls.c [new file with mode: 0644]
ext/opcache/Optimizer/pass1_5.c
ext/opcache/Optimizer/zend_optimizer.c
ext/opcache/Optimizer/zend_optimizer.h

diff --git a/ext/opcache/Optimizer/optimize_func_calls.c b/ext/opcache/Optimizer/optimize_func_calls.c
new file mode 100644 (file)
index 0000000..ee271f2
--- /dev/null
@@ -0,0 +1,136 @@
+/* pass 4
+ * - optimize INIT_FCALL_BY_NAME to DO_FCALL
+ */
+
+typedef struct _optimizer_call_info {
+       zend_function *func;
+       zend_op       *opline;
+} optimizer_call_info;
+
+static void optimize_func_calls(zend_op_array *op_array, zend_persistent_script *script TSRMLS_CC) {
+       zend_op *opline = op_array->opcodes;
+       zend_op *end = opline + op_array->last;
+       int call = 0;
+#if ZEND_EXTENSION_API_NO > PHP_5_4_X_API_NO
+       optimizer_call_info *call_stack = ecalloc(op_array->nested_calls + 1, sizeof(optimizer_call_info));
+#else
+       int stack_size = 4;
+       optimizer_call_info *call_stack = ecalloc(stack_size, sizeof(optimizer_call_info));
+#endif
+
+       while (opline < end) {
+               switch (opline->opcode) {
+                       case ZEND_INIT_FCALL_BY_NAME:
+                       case ZEND_INIT_NS_FCALL_BY_NAME:
+                               if (ZEND_OP2_TYPE(opline) == IS_CONST) {
+                                       zend_function *func;
+                                       zval *function_name = &op_array->literals[opline->op2.constant + 1].constant;
+                                       if ((zend_hash_quick_find(&script->function_table,
+                                                       Z_STRVAL_P(function_name), Z_STRLEN_P(function_name) + 1,
+                                                       Z_HASH_P(function_name), (void **)&func) == SUCCESS)) {
+                                               call_stack[call].func = func;
+                                       }
+                               }
+                               /* break missing intentionally */
+                       case ZEND_NEW:
+                       case ZEND_INIT_METHOD_CALL:
+                       case ZEND_INIT_STATIC_METHOD_CALL:
+                               call_stack[call].opline = opline;
+                               call++;
+#if ZEND_EXTENSION_API_NO < PHP_5_5_X_API_NO
+                               if (call == stack_size) {
+                                       stack_size += 4;
+                                       call_stack = erealloc(call_stack, sizeof(optimizer_call_info) * stack_size);
+                                       memset(call_stack + 4, 0, 4 * sizeof(optimizer_call_info));
+                               }
+#endif
+                               break;
+                       case ZEND_DO_FCALL_BY_NAME:
+                               call--;
+                               if (call_stack[call].func && call_stack[call].opline) {
+                                       zend_op *fcall = call_stack[call].opline;
+
+                                       opline->opcode = ZEND_DO_FCALL;
+                                       ZEND_OP1_TYPE(opline) = IS_CONST;
+                                       opline->op1.constant = fcall->op2.constant + 1;
+                                       op_array->literals[fcall->op2.constant + 1].cache_slot = op_array->literals[fcall->op2.constant].cache_slot;
+                                       literal_dtor(&ZEND_OP2_LITERAL(fcall));
+                                       if (fcall->opcode == ZEND_INIT_NS_FCALL_BY_NAME) {
+                                               literal_dtor(&op_array->literals[fcall->op2.constant + 2].constant);
+                                       }
+                                       MAKE_NOP(fcall);
+                               } else if (opline->extended_value == 0 &&
+                                          call_stack[call].opline &&
+                                          call_stack[call].opline == ZEND_INIT_FCALL_BY_NAME &&
+                                          ZEND_OP2_TYPE(call_stack[call].opline) == IS_CONST) {
+
+                                       zend_op *fcall = call_stack[call].opline;
+
+                                       opline->opcode = ZEND_DO_FCALL;
+                                       ZEND_OP1_TYPE(opline) = IS_CONST;
+                                       opline->op1.constant = fcall->op2.constant + 1;
+                                       op_array->literals[fcall->op2.constant + 1].cache_slot = op_array->literals[fcall->op2.constant].cache_slot;
+                                       literal_dtor(&ZEND_OP2_LITERAL(fcall));
+                                       MAKE_NOP(fcall);
+                               }
+                               call_stack[call].func = NULL;
+                               call_stack[call].opline = NULL;
+                               break;
+                       case ZEND_FETCH_FUNC_ARG:
+                       case ZEND_FETCH_OBJ_FUNC_ARG:
+                       case ZEND_FETCH_DIM_FUNC_ARG:
+                               if (call_stack[call - 1].func) {
+                                       if (ARG_SHOULD_BE_SENT_BY_REF(call_stack[call].func, (opline->extended_value & ZEND_FETCH_ARG_MASK))) {
+                                               opline->extended_value = 0;
+                                               opline->opcode -= 9;
+                                       } else {
+                                               opline->extended_value = 0;
+                                               opline->opcode -= 12;
+                                       }
+                               }
+                               break;
+                       case ZEND_SEND_VAL:
+                               if (opline->extended_value == ZEND_DO_FCALL_BY_NAME && call_stack[call - 1].func) {
+                                       if (ARG_MUST_BE_SENT_BY_REF(call_stack[call].func, opline->op2.num)) {
+                                               /* We won't convert it into_DO_FCALL to emit error at run-time */
+                                               call_stack[call].opline = NULL;
+                                       } else {
+                                               opline->extended_value = ZEND_DO_FCALL;
+                                       }
+                               }
+                               break;
+                       case ZEND_SEND_VAR:
+                               if (opline->extended_value == ZEND_DO_FCALL_BY_NAME && call_stack[call - 1].func) {
+                                       if (ARG_SHOULD_BE_SENT_BY_REF(call_stack[call].func, opline->op2.num)) {
+                                               opline->opcode = ZEND_SEND_REF;
+                                       }
+                                       opline->extended_value = ZEND_DO_FCALL;
+                               }
+                               break;
+                       case ZEND_SEND_VAR_NO_REF:
+                               if (!(opline->extended_value & ZEND_ARG_COMPILE_TIME_BOUND) && call_stack[call - 1].func) {
+                                       if (ARG_SHOULD_BE_SENT_BY_REF(call_stack[call].func, opline->op2.num)) {
+                                               opline->extended_value |= ZEND_ARG_COMPILE_TIME_BOUND | ZEND_ARG_SEND_BY_REF;
+                                       } else if (opline->extended_value) {
+                                               opline->extended_value |= ZEND_ARG_COMPILE_TIME_BOUND;
+                                       } else {
+                                               opline->opcode = ZEND_SEND_VAR;
+                                               opline->extended_value = ZEND_DO_FCALL;
+                                       }
+                               }
+                               break;
+                       case ZEND_SEND_REF:
+                               if (opline->extended_value == ZEND_DO_FCALL_BY_NAME && call_stack[call - 1].func) {
+                                       /* We won't handle run-time pass by reference */
+                                       call_stack[call].opline = NULL;
+                               }
+                               break;
+
+                       default:
+                               break;
+               }
+               opline++;
+       }
+
+       efree(call_stack);
+}
index 3a32970650bdc0f78628c101dda70b9b807a5887..ec5a786577371be5e803a66a9f103606979a3eca 100644 (file)
@@ -3,7 +3,6 @@
  * - perform compile-time evaluation of constant binary and unary operations
  * - optimize series of ADD_STRING and/or ADD_CHAR
  * - convert CAST(IS_BOOL,x) into BOOL(x)
- * - convert INTI_FCALL_BY_NAME, DO_FCALL_BY_NAME into DO_FCALL
  */
 
 if (ZEND_OPTIMIZER_PASS_1 & OPTIMIZATION_LEVEL) {
@@ -374,23 +373,6 @@ if (ZEND_OPTIMIZER_PASS_1 & OPTIMIZATION_LEVEL) {
                        }
                        break;
 
-               case ZEND_INIT_FCALL_BY_NAME:
-                       if (opline->extended_value == 0 /* not method */ &&
-                               ZEND_OP1_TYPE(opline) == IS_UNUSED &&
-                               ZEND_OP2_TYPE(opline) == IS_CONST) {
-                               if ((opline + 1)->opcode == ZEND_DO_FCALL_BY_NAME &&
-                                       (opline + 1)->extended_value == 0) {
-                                       (opline + 1)->opcode = ZEND_DO_FCALL;
-                                       COPY_NODE((opline + 1)->op1, opline->op2);
-                                       zend_str_tolower(Z_STRVAL(ZEND_OP1_LITERAL(opline + 1)), Z_STRLEN(ZEND_OP1_LITERAL(opline + 1)));
-#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
-                                       Z_HASH_P(&ZEND_OP1_LITERAL(opline + 1)) = zend_hash_func(Z_STRVAL(ZEND_OP1_LITERAL(opline + 1)), Z_STRLEN(ZEND_OP1_LITERAL(opline + 1)) + 1);
-                                       op_array->literals[(opline + 1)->op1.constant].cache_slot = op_array->last_cache_slot++;
-#endif
-                                       MAKE_NOP(opline);
-                               }
-                       }
-                       break;
                case ZEND_DO_FCALL:
                        /* define("name", scalar); */
                        if (collect_constants &&
index fc0066e602bf8fdac980426cb91a46c474e61812..9cbcd06ac85d1de09cf0eb31c086812452d6370a 100644 (file)
@@ -113,6 +113,7 @@ int zend_optimizer_add_literal(zend_op_array *op_array, const zval *zv TSRMLS_DC
 #include "Optimizer/block_pass.c"\r
 #include "Optimizer/optimize_temp_vars_5.c"\r
 #include "Optimizer/compact_literals.c"\r
+#include "Optimizer/optimize_func_calls.c"\r
 \r
 static void zend_optimize(zend_op_array           *op_array,\r
                           zend_persistent_script  *script,\r
@@ -128,7 +129,6 @@ static void zend_optimize(zend_op_array           *op_array,
         * - perform compile-time evaluation of constant binary and unary operations\r
         * - optimize series of ADD_STRING and/or ADD_CHAR\r
         * - convert CAST(IS_BOOL,x) into BOOL(x)\r
-        * - convert INTI_FCALL_BY_NAME + DO_FCALL_BY_NAME into DO_FCALL\r
         */\r
 #include "Optimizer/pass1_5.c"\r
 \r
@@ -146,6 +146,13 @@ static void zend_optimize(zend_op_array           *op_array,
         */\r
 #include "Optimizer/pass3.c"\r
 \r
+       /* pass 4:\r
+        * - INIT_FCALL_BY_NAME -> DO_FCALL\r
+        */\r
+       if (ZEND_OPTIMIZER_PASS_4 & OPTIMIZATION_LEVEL) {\r
+               optimize_func_calls(op_array, script TSRMLS_CC);\r
+       }\r
+\r
        /* pass 5:\r
         * - CFG optimization\r
         */\r
index 4be37ba397a08c49b036f049a9a1bf337714af2f..5a3d715ac9b1a91462b21cb2cecd2dbf6e789d69 100644 (file)
@@ -28,7 +28,7 @@
 #define ZEND_OPTIMIZER_PASS_1          (1<<0)   /* CSE, STRING construction     */\r
 #define ZEND_OPTIMIZER_PASS_2          (1<<1)   /* Constant conversion and jumps */\r
 #define ZEND_OPTIMIZER_PASS_3          (1<<2)   /* ++, +=, series of jumps      */\r
-#define ZEND_OPTIMIZER_PASS_4          (1<<3)\r
+#define ZEND_OPTIMIZER_PASS_4          (1<<3)   /* INIT_FCALL_BY_NAME -> DO_FCALL */\r
 #define ZEND_OPTIMIZER_PASS_5          (1<<4)   /* CFG based optimization       */\r
 #define ZEND_OPTIMIZER_PASS_6          (1<<5)\r
 #define ZEND_OPTIMIZER_PASS_7          (1<<6)\r