]> granicus.if.org Git - php/commitdiff
Added an optimization of class constants and constant calls to some internal functions
authorDmitry Stogov <dmitry@zend.com>
Tue, 26 Nov 2013 17:37:31 +0000 (21:37 +0400)
committerDmitry Stogov <dmitry@zend.com>
Tue, 26 Nov 2013 17:37:31 +0000 (21:37 +0400)
ext/opcache/Optimizer/pass1_5.c
ext/opcache/Optimizer/zend_optimizer.c

index fcaf29bb5858895fbbbe3fd3c789f9636ac1d6e5..bd8af47a95e3fd85a1e43d69c1a12e0bbfff6060 100644 (file)
@@ -3,8 +3,15 @@
  * - 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)
+ * - pre-evaluate constant function calls
  */
 
+#if ZEND_EXTENSION_API_NO > PHP_5_2_X_API_NO
+# define ZEND_IS_CONSTANT_TYPE(t)      (((t) & IS_CONSTANT_TYPE_MASK) == IS_CONSTANT)
+#else
+# define ZEND_IS_CONSTANT_TYPE(t)      ((t) == IS_CONSTANT)
+#endif
+
 if (ZEND_OPTIMIZER_PASS_1 & OPTIMIZATION_LEVEL) {
        int i = 0;
        zend_op *opline = op_array->opcodes;
@@ -246,9 +253,184 @@ if (ZEND_OPTIMIZER_PASS_1 & OPTIMIZATION_LEVEL) {
                                MAKE_NOP(opline);
                                replace_tmp_by_const(op_array, opline, tv, &c TSRMLS_CC);
                        }
+
+#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
+                       /* class constant */
+                       if (ZEND_OP1_TYPE(opline) != IS_UNUSED &&
+                           ZEND_OP2_TYPE(opline) == IS_CONST &&
+                               ZEND_OP2_LITERAL(opline).type == IS_STRING) {
+
+                               zend_class_entry **pce = NULL;
+
+                               if (ZEND_OP1_TYPE(opline) == IS_CONST &&
+                               ZEND_OP1_LITERAL(opline).type == IS_STRING) {
+                                       /* for A::B */
+                                       if (op_array->scope && 
+                                               !strncasecmp(Z_STRVAL(ZEND_OP1_LITERAL(opline)),
+                                               op_array->scope->name, Z_STRLEN(ZEND_OP1_LITERAL(opline)) + 1)) {
+                                               pce = &op_array->scope;
+                                       } else { 
+                                               if (zend_hash_quick_find(EG(class_table), 
+                                                               Z_STRVAL(op_array->literals[opline->op1.constant + 1].constant), 
+                                                               Z_STRLEN(op_array->literals[opline->op1.constant].constant) + 1, 
+                                                               Z_HASH_P(&op_array->literals[opline->op1.constant + 1].constant),
+                                                               (void **)&pce) == FAILURE) {
+                                                       break;
+                                               }
+                                       }
+                               } else if (op_array->scope &&
+                                       ZEND_OP1_TYPE(opline) == IS_VAR &&
+                                       (opline - 1)->opcode == ZEND_FETCH_CLASS && 
+                                       (ZEND_OP1_TYPE(opline - 1) == IS_UNUSED &&
+                                       ((opline - 1)->extended_value & ~ZEND_FETCH_CLASS_NO_AUTOLOAD) == ZEND_FETCH_CLASS_SELF) &&
+                                       ZEND_RESULT((opline - 1)).var == ZEND_OP1(opline).var) {
+                                       /* for self::B */
+                                       pce = &op_array->scope;
+                               }
+
+                               if (pce) {
+                                       zend_uint tv = ZEND_RESULT(opline).var;
+                                       zval **c, t;
+
+                                       if (zend_hash_find(&(*pce)->constants_table,
+                                                       Z_STRVAL(ZEND_OP2_LITERAL(opline)),
+                                                       Z_STRLEN(ZEND_OP2_LITERAL(opline)) + 1,
+                                                       (void **) &c) == SUCCESS) {
+                                               if (ZEND_IS_CONSTANT_TYPE(Z_TYPE_PP(c))) { 
+                                                       if (!zend_get_persistent_constant(Z_STRVAL_PP(c), Z_STRLEN_PP(c), &t, 1 TSRMLS_CC) ||
+                                                           ZEND_IS_CONSTANT_TYPE(Z_TYPE(t))) {
+                                                               break;
+                                                       }
+                                               } else {
+                                                       t = **c;
+                                                       zval_copy_ctor(&t);
+                                               }
+
+                                               if (ZEND_OP1_TYPE(opline) == IS_CONST) {
+                                                       literal_dtor(&ZEND_OP1_LITERAL(opline));
+                                               } else {
+                                                       MAKE_NOP((opline - 1));
+                                               }
+                                               literal_dtor(&ZEND_OP2_LITERAL(opline));
+                                               MAKE_NOP(opline);
+                                               replace_tmp_by_const(op_array, opline, tv, &t TSRMLS_CC);
+                                       }
+                               }
+                       }
+#endif
                        break;
 
                case ZEND_DO_FCALL:
+                       /* pre-evaluate constant functions:
+                          defined(x)
+                          constant(x)
+                          function_exists(x)
+                          is_callable(x)
+                          extension_loaded(x)
+                       */
+                       if (opline->extended_value == 1 && (opline - 1)->opcode == ZEND_SEND_VAL &&
+                               ZEND_OP1_TYPE(opline - 1) == IS_CONST && ZEND_OP1_LITERAL(opline - 1).type == IS_STRING &&
+                               ZEND_OP1_TYPE(opline) == IS_CONST && ZEND_OP1_LITERAL(opline).type == IS_STRING) {
+                               if ((Z_STRLEN(ZEND_OP1_LITERAL(opline)) == sizeof("function_exists")-1 &&
+                                       !memcmp(Z_STRVAL(ZEND_OP1_LITERAL(opline)),
+                                               "function_exists", sizeof("function_exists")-1)) ||
+                                       (Z_STRLEN(ZEND_OP1_LITERAL(opline)) == sizeof("is_callable")-1 &&
+                                       !memcmp(Z_STRVAL(ZEND_OP1_LITERAL(opline)),
+                                               "is_callable", sizeof("is_callable")))) {
+                                       zend_internal_function *func;
+                                       char *lc_name = zend_str_tolower_dup(
+                                                       Z_STRVAL(ZEND_OP1_LITERAL(opline - 1)), Z_STRLEN(ZEND_OP1_LITERAL(opline - 1)));
+                                       
+                                       if (zend_hash_find(EG(function_table), lc_name, Z_STRLEN(ZEND_OP1_LITERAL(opline - 1)) + 1,
+                                                               (void *)&func) == SUCCESS &&
+                                                       func->type == ZEND_INTERNAL_FUNCTION &&
+                                                       func->module->type == MODULE_PERSISTENT) {
+                                               zval t;
+                                               ZVAL_BOOL(&t, 1);
+                                               if (replace_var_by_const(op_array, opline + 1, ZEND_RESULT(opline).var, &t TSRMLS_CC)) {
+                                                       literal_dtor(&ZEND_OP1_LITERAL(opline - 1));
+                                                       MAKE_NOP((opline - 1));
+                                                       literal_dtor(&ZEND_OP1_LITERAL(opline));
+                                                       MAKE_NOP(opline);
+                                               }
+                                       }
+                                       efree(lc_name);
+                               } else if (Z_STRLEN(ZEND_OP1_LITERAL(opline)) == sizeof("extension_loaded")-1 &&
+                                       !memcmp(Z_STRVAL(ZEND_OP1_LITERAL(opline)),
+                                               "extension_loaded", sizeof("extension_loaded")-1)) {
+                                       zval t;
+                                       zend_module_entry *m;
+                                       char *lc_name = zend_str_tolower_dup(
+                                                       Z_STRVAL(ZEND_OP1_LITERAL(opline - 1)), Z_STRLEN(ZEND_OP1_LITERAL(opline - 1)));
+
+                                       if (zend_hash_find(&module_registry,
+                                                       lc_name, Z_STRLEN(ZEND_OP1_LITERAL(opline - 1)) + 1, (void *)&m) == FAILURE) {
+                                               if (!PG(enable_dl)) {
+                                                       break;
+                                               } else {
+                                                       ZVAL_BOOL(&t, 0);
+                                               }
+                                       } else {
+                                               if (m->type == MODULE_PERSISTENT) {
+                                                       ZVAL_BOOL(&t, 1);
+                                               } else {
+                                                       break;
+                                               }
+                                       } 
+
+                                       if (replace_var_by_const(op_array, opline + 1, ZEND_RESULT(opline).var, &t TSRMLS_CC)) {
+                                               literal_dtor(&ZEND_OP1_LITERAL(opline - 1));
+                                               MAKE_NOP((opline - 1));
+                                               literal_dtor(&ZEND_OP1_LITERAL(opline));
+                                               MAKE_NOP(opline);
+                                       }
+                                       efree(lc_name);
+                               } else if (Z_STRLEN(ZEND_OP1_LITERAL(opline)) == sizeof("defined")-1 &&
+                                       !memcmp(Z_STRVAL(ZEND_OP1_LITERAL(opline)),
+                                               "defined", sizeof("defined")-1)) {
+                                       zval t;
+
+                                       if (zend_get_persistent_constant(Z_STRVAL(ZEND_OP1_LITERAL(opline - 1)),
+                                               Z_STRLEN(ZEND_OP1_LITERAL(opline - 1)), &t, 0 TSRMLS_CC)) {
+
+                                               ZVAL_BOOL(&t, 1);
+                                               if (replace_var_by_const(op_array, opline + 1, ZEND_RESULT(opline).var, &t TSRMLS_CC)) {
+                                                       literal_dtor(&ZEND_OP1_LITERAL(opline - 1));
+                                                       MAKE_NOP((opline - 1));
+                                                       literal_dtor(&ZEND_OP1_LITERAL(opline));
+                                                       MAKE_NOP(opline);
+                                               }
+                                       }
+                               } else if (Z_STRLEN(ZEND_OP1_LITERAL(opline)) == sizeof("constant")-1 &&
+                                       !memcmp(Z_STRVAL(ZEND_OP1_LITERAL(opline)),
+                                               "constant", sizeof("constant")-1)) {
+                                       zval t;
+                                       
+                                       if (zend_get_persistent_constant(Z_STRVAL(ZEND_OP1_LITERAL(opline - 1)),
+                                               Z_STRLEN(ZEND_OP1_LITERAL(opline - 1)), &t, 0 TSRMLS_CC)) {
+                                               if (replace_var_by_const(op_array, opline + 1, ZEND_RESULT(opline).var, &t TSRMLS_CC)) {
+                                                       literal_dtor(&ZEND_OP1_LITERAL(opline - 1));
+                                                       MAKE_NOP((opline - 1));
+                                                       literal_dtor(&ZEND_OP1_LITERAL(opline));
+                                                       MAKE_NOP(opline);
+                                               }
+                                       }
+                               } else if (Z_STRLEN(ZEND_OP1_LITERAL(opline)) == sizeof("strlen")-1 &&
+                                       !memcmp(Z_STRVAL(ZEND_OP1_LITERAL(opline)),
+                                               "strlen", sizeof("strlen")-1)) {
+                                       zval t;
+
+                                       ZVAL_LONG(&t, Z_STRLEN(ZEND_OP1_LITERAL(opline - 1)));
+                                       if (replace_var_by_const(op_array, opline + 1, ZEND_RESULT(opline).var, &t TSRMLS_CC)) {
+                                               literal_dtor(&ZEND_OP1_LITERAL(opline - 1));
+                                               MAKE_NOP((opline - 1));
+                                               literal_dtor(&ZEND_OP1_LITERAL(opline));
+                                               MAKE_NOP(opline);
+                                       }
+                               }
+                               break;
+                       }
+                       
                        /* define("name", scalar); */
                        if (collect_constants &&
                            opline->extended_value == 2 &&
index 68184d9db42217ba74d3ba859b6e25a557bf90e1..0426f63514d606dec3ff11599c421b5d4ac29007 100644 (file)
@@ -300,6 +300,61 @@ check_numeric:
 #endif\r
 }\r
 \r
+static int replace_var_by_const(zend_op_array *op_array,\r
+                                zend_op       *opline,\r
+                                zend_uint      var,\r
+                                zval          *val TSRMLS_DC)\r
+{\r
+       zend_op *end = op_array->opcodes + op_array->last;\r
+\r
+       while (opline < end) {\r
+               if (ZEND_OP1_TYPE(opline) == IS_VAR &&\r
+                       ZEND_OP1(opline).var == var) {\r
+                       switch (opline->opcode) {\r
+                               case ZEND_FETCH_DIM_W:\r
+                               case ZEND_FETCH_DIM_RW:\r
+                               case ZEND_FETCH_DIM_FUNC_ARG:\r
+                               case ZEND_FETCH_DIM_UNSET:\r
+                               case ZEND_ASSIGN_DIM:\r
+#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO\r
+                               case ZEND_SEPARATE:\r
+#endif\r
+                                       return 0;\r
+                               case ZEND_SEND_VAR_NO_REF:\r
+                                       if (opline->extended_value & ZEND_ARG_COMPILE_TIME_BOUND) {\r
+                                               if (opline->extended_value & ZEND_ARG_SEND_BY_REF) {\r
+                                                       return 0;\r
+                                               }\r
+                                               opline->extended_value = ZEND_DO_FCALL;\r
+                                       } else {\r
+                                               opline->extended_value = ZEND_DO_FCALL_BY_NAME;\r
+                                       }\r
+                                       opline->opcode = ZEND_SEND_VAL;\r
+                                       break;\r
+                               default:\r
+                                       break;\r
+                       } \r
+                       update_op1_const(op_array, opline, val TSRMLS_CC);\r
+                       break;\r
+               }\r
+               \r
+               if (ZEND_OP2_TYPE(opline) == IS_VAR &&\r
+                       ZEND_OP2(opline).var == var) {\r
+                       switch (opline->opcode) {\r
+                               case ZEND_ASSIGN_REF:\r
+                                       return 0;\r
+                               default:\r
+                                       break;\r
+                       }\r
+                       update_op2_const(op_array, opline, val TSRMLS_CC);\r
+                       break;\r
+               }\r
+               opline++;\r
+       }\r
+\r
+       return 1;\r
+}\r
+\r
 static void replace_tmp_by_const(zend_op_array *op_array,\r
                                  zend_op       *opline,\r
                                  zend_uint      var,\r
@@ -355,6 +410,7 @@ static void zend_optimize(zend_op_array           *op_array,
         * - convert non-numeric constants to numeric constants in numeric operators\r
         * - optimize constant conditional JMPs\r
         * - optimize static BRKs and CONTs\r
+        * - pre-evaluate constant function calls\r
         */\r
 #include "Optimizer/pass2.c"\r
 \r