From: Dmitry Stogov Date: Tue, 26 Nov 2013 17:37:31 +0000 (+0400) Subject: Added an optimization of class constants and constant calls to some internal functions X-Git-Tag: php-5.6.0alpha1~179 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=f000624864a5333825ef8ff0fef38afd4200433c;p=php Added an optimization of class constants and constant calls to some internal functions --- diff --git a/ext/opcache/Optimizer/pass1_5.c b/ext/opcache/Optimizer/pass1_5.c index fcaf29bb58..bd8af47a95 100644 --- a/ext/opcache/Optimizer/pass1_5.c +++ b/ext/opcache/Optimizer/pass1_5.c @@ -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 && diff --git a/ext/opcache/Optimizer/zend_optimizer.c b/ext/opcache/Optimizer/zend_optimizer.c index 68184d9db4..0426f63514 100644 --- a/ext/opcache/Optimizer/zend_optimizer.c +++ b/ext/opcache/Optimizer/zend_optimizer.c @@ -300,6 +300,61 @@ check_numeric: #endif } +static int replace_var_by_const(zend_op_array *op_array, + zend_op *opline, + zend_uint var, + zval *val TSRMLS_DC) +{ + zend_op *end = op_array->opcodes + op_array->last; + + while (opline < end) { + if (ZEND_OP1_TYPE(opline) == IS_VAR && + ZEND_OP1(opline).var == var) { + switch (opline->opcode) { + case ZEND_FETCH_DIM_W: + case ZEND_FETCH_DIM_RW: + case ZEND_FETCH_DIM_FUNC_ARG: + case ZEND_FETCH_DIM_UNSET: + case ZEND_ASSIGN_DIM: +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + case ZEND_SEPARATE: +#endif + return 0; + case ZEND_SEND_VAR_NO_REF: + if (opline->extended_value & ZEND_ARG_COMPILE_TIME_BOUND) { + if (opline->extended_value & ZEND_ARG_SEND_BY_REF) { + return 0; + } + opline->extended_value = ZEND_DO_FCALL; + } else { + opline->extended_value = ZEND_DO_FCALL_BY_NAME; + } + opline->opcode = ZEND_SEND_VAL; + break; + default: + break; + } + update_op1_const(op_array, opline, val TSRMLS_CC); + break; + } + + if (ZEND_OP2_TYPE(opline) == IS_VAR && + ZEND_OP2(opline).var == var) { + switch (opline->opcode) { + case ZEND_ASSIGN_REF: + return 0; + default: + break; + } + update_op2_const(op_array, opline, val TSRMLS_CC); + break; + } + opline++; + } + + return 1; +} + static void replace_tmp_by_const(zend_op_array *op_array, zend_op *opline, zend_uint var, @@ -355,6 +410,7 @@ static void zend_optimize(zend_op_array *op_array, * - convert non-numeric constants to numeric constants in numeric operators * - optimize constant conditional JMPs * - optimize static BRKs and CONTs + * - pre-evaluate constant function calls */ #include "Optimizer/pass2.c"