Commit: f000624864a5333825ef8ff0fef38afd4200433c
Author: Dmitry Stogov <[email protected]> Tue, 26 Nov 2013 21:37:31 +0400
Parents: e7132597a68224d1a2008d005c7d1b0254c512e2
Branches: PHP-5.6 master
Link: http://git.php.net/?p=php-src.git;a=commitdiff;h=f000624864a5333825ef8ff0fef38afd4200433c
Log:
Added an optimization of class constants and constant calls to some internal functions
Changed paths:
M ext/opcache/Optimizer/pass1_5.c
M ext/opcache/Optimizer/zend_optimizer.c
diff --git a/ext/opcache/Optimizer/pass1_5.c b/ext/opcache/Optimizer/pass1_5.c
index fcaf29b..bd8af47 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 68184d9..0426f63 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"