From: Dmitry Stogov Date: Thu, 18 Apr 2013 18:12:31 +0000 (+0400) Subject: Added an optimization pass to convert FCALL_BY_NAME into DO_FCALL. X-Git-Tag: php-5.6.0alpha1~448^2~31^2~1 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=edfeab8bf2750da4616f802650d28b0774eccba3;p=php Added an optimization pass to convert FCALL_BY_NAME into DO_FCALL. --- diff --git a/ext/opcache/Optimizer/optimize_func_calls.c b/ext/opcache/Optimizer/optimize_func_calls.c new file mode 100644 index 0000000000..ee271f22aa --- /dev/null +++ b/ext/opcache/Optimizer/optimize_func_calls.c @@ -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); +} diff --git a/ext/opcache/Optimizer/pass1_5.c b/ext/opcache/Optimizer/pass1_5.c index 3a32970650..ec5a786577 100644 --- a/ext/opcache/Optimizer/pass1_5.c +++ b/ext/opcache/Optimizer/pass1_5.c @@ -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 && diff --git a/ext/opcache/Optimizer/zend_optimizer.c b/ext/opcache/Optimizer/zend_optimizer.c index fc0066e602..9cbcd06ac8 100644 --- a/ext/opcache/Optimizer/zend_optimizer.c +++ b/ext/opcache/Optimizer/zend_optimizer.c @@ -113,6 +113,7 @@ int zend_optimizer_add_literal(zend_op_array *op_array, const zval *zv TSRMLS_DC #include "Optimizer/block_pass.c" #include "Optimizer/optimize_temp_vars_5.c" #include "Optimizer/compact_literals.c" +#include "Optimizer/optimize_func_calls.c" static void zend_optimize(zend_op_array *op_array, zend_persistent_script *script, @@ -128,7 +129,6 @@ static void zend_optimize(zend_op_array *op_array, * - 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 */ #include "Optimizer/pass1_5.c" @@ -146,6 +146,13 @@ static void zend_optimize(zend_op_array *op_array, */ #include "Optimizer/pass3.c" + /* pass 4: + * - INIT_FCALL_BY_NAME -> DO_FCALL + */ + if (ZEND_OPTIMIZER_PASS_4 & OPTIMIZATION_LEVEL) { + optimize_func_calls(op_array, script TSRMLS_CC); + } + /* pass 5: * - CFG optimization */ diff --git a/ext/opcache/Optimizer/zend_optimizer.h b/ext/opcache/Optimizer/zend_optimizer.h index 4be37ba397..5a3d715ac9 100644 --- a/ext/opcache/Optimizer/zend_optimizer.h +++ b/ext/opcache/Optimizer/zend_optimizer.h @@ -28,7 +28,7 @@ #define ZEND_OPTIMIZER_PASS_1 (1<<0) /* CSE, STRING construction */ #define ZEND_OPTIMIZER_PASS_2 (1<<1) /* Constant conversion and jumps */ #define ZEND_OPTIMIZER_PASS_3 (1<<2) /* ++, +=, series of jumps */ -#define ZEND_OPTIMIZER_PASS_4 (1<<3) +#define ZEND_OPTIMIZER_PASS_4 (1<<3) /* INIT_FCALL_BY_NAME -> DO_FCALL */ #define ZEND_OPTIMIZER_PASS_5 (1<<4) /* CFG based optimization */ #define ZEND_OPTIMIZER_PASS_6 (1<<5) #define ZEND_OPTIMIZER_PASS_7 (1<<6)