Skip to content

Commit 40e2a53

Browse files
re-use sprintf() optimisation for printf()
1 parent ca4a841 commit 40e2a53

File tree

2 files changed

+69
-1
lines changed

2 files changed

+69
-1
lines changed

Zend/Optimizer/block_pass.c

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -431,6 +431,53 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array
431431
case ZEND_CASE:
432432
case ZEND_CASE_STRICT:
433433
case ZEND_COPY_TMP:
434+
/* Check for printf optimization from `zend_compile_func_printf()`
435+
* where the result of `printf()` is actually unused and remove the
436+
* superflous COPY_TMP, STRLEN and FREE opcodes:
437+
* T1 = COPY_TMP T0
438+
* ECHO T0
439+
* T2 = STRLEN T1
440+
* FREE T2
441+
*/
442+
if (opline->op1_type == IS_TMP_VAR &&
443+
opline + 1 < end && (opline + 1)->opcode == ZEND_ECHO &&
444+
opline + 2 < end && (opline + 2)->opcode == ZEND_STRLEN &&
445+
opline + 3 < end && (opline + 3)->opcode == ZEND_FREE) {
446+
447+
zend_op *echo_op = opline + 1;
448+
zend_op *strlen_op = opline + 2;
449+
zend_op *free_op = opline + 3;
450+
451+
/* Verify the pattern:
452+
* - ECHO uses the same source as COPY_TMP
453+
* - STRLEN uses the result of COPY_TMP
454+
* - FREE uses the result of STRLEN
455+
*/
456+
if (echo_op->op1_type == IS_TMP_VAR &&
457+
echo_op->op1.var == opline->op1.var &&
458+
strlen_op->op1_type == IS_TMP_VAR &&
459+
strlen_op->op1.var == opline->result.var &&
460+
free_op->op1_type == IS_TMP_VAR &&
461+
free_op->op1.var == strlen_op->result.var) {
462+
463+
/* Remove COPY_TMP, STRLEN, and FREE */
464+
MAKE_NOP(opline);
465+
MAKE_NOP(strlen_op);
466+
MAKE_NOP(free_op);
467+
468+
/* Update source tracking */
469+
if (opline->result_type == IS_TMP_VAR) {
470+
VAR_SOURCE(opline->result) = NULL;
471+
}
472+
if (strlen_op->result_type == IS_TMP_VAR) {
473+
VAR_SOURCE(strlen_op->result) = NULL;
474+
}
475+
476+
++(*opt_count);
477+
break;
478+
}
479+
}
480+
434481
if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) {
435482
/* Variable will be deleted later by FREE, so we can't optimize it */
436483
Tsource[VAR_NUM(opline->op1.var)] = NULL;
@@ -538,7 +585,7 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array
538585
}
539586
}
540587
break;
541-
588+
542589
case ZEND_BOOL:
543590
case ZEND_BOOL_NOT:
544591
optimize_bool:

Zend/zend_compile.c

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4954,6 +4954,25 @@ static zend_result zend_compile_func_sprintf(znode *result, zend_ast_list *args)
49544954
return SUCCESS;
49554955
}
49564956

4957+
static zend_result zend_compile_func_printf(znode *result, zend_ast_list *args) /* {{{ */
4958+
{
4959+
znode rope_result;
4960+
if (zend_compile_func_sprintf(&rope_result, args) != SUCCESS) {
4961+
return FAILURE;
4962+
}
4963+
4964+
/* printf() returns the amount of bytes written, so just an ECHO of the resulting sprintf()
4965+
* optimisation might not be enough. At this early stage we can't detect if the result is
4966+
* actually used, so we just emit the opcodes and cleanup if they are not used in the
4967+
* optimizer later. */
4968+
znode copy;
4969+
zend_emit_op_tmp(&copy, ZEND_COPY_TMP, &rope_result, NULL);
4970+
zend_emit_op(NULL, ZEND_ECHO, &rope_result, NULL);
4971+
zend_emit_op_tmp(result, ZEND_STRLEN, &copy, NULL);
4972+
4973+
return SUCCESS;
4974+
}
4975+
49574976
static zend_result zend_compile_func_clone(znode *result, zend_ast_list *args)
49584977
{
49594978
znode arg_node;
@@ -5036,6 +5055,8 @@ static zend_result zend_try_compile_special_func_ex(znode *result, zend_string *
50365055
return zend_compile_func_array_key_exists(result, args);
50375056
} else if (zend_string_equals_literal(lcname, "sprintf")) {
50385057
return zend_compile_func_sprintf(result, args);
5058+
} else if (zend_string_equals_literal(lcname, "printf")) {
5059+
return zend_compile_func_printf(result, args);
50395060
} else if (zend_string_equals(lcname, ZSTR_KNOWN(ZEND_STR_CLONE))) {
50405061
return zend_compile_func_clone(result, args);
50415062
} else {

0 commit comments

Comments
 (0)