Skip to content
Closed
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Zend/Optimizer/block_pass.c
Original file line number Diff line number Diff line change
Expand Up @@ -538,7 +538,7 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array
}
}
break;

case ZEND_BOOL:
case ZEND_BOOL_NOT:
optimize_bool:
Expand Down
7 changes: 5 additions & 2 deletions Zend/Optimizer/dce.c
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ static inline bool may_have_side_effects(
case ZEND_FUNC_NUM_ARGS:
case ZEND_FUNC_GET_ARGS:
case ZEND_ARRAY_KEY_EXISTS:
case ZEND_COPY_TMP:
/* No side effects */
return 0;
case ZEND_FREE:
Expand Down Expand Up @@ -425,10 +426,12 @@ static bool dce_instr(context *ctx, zend_op *opline, zend_ssa_op *ssa_op) {
return 0;
}

if ((opline->op1_type & (IS_VAR|IS_TMP_VAR))&& !is_var_dead(ctx, ssa_op->op1_use)) {
if ((opline->op1_type & (IS_VAR|IS_TMP_VAR)) && !is_var_dead(ctx, ssa_op->op1_use)) {
if (!try_remove_var_def(ctx, ssa_op->op1_use, ssa_op->op1_use_chain, opline)) {
if (may_be_refcounted(ssa->var_info[ssa_op->op1_use].type)
&& opline->opcode != ZEND_CASE && opline->opcode != ZEND_CASE_STRICT) {
&& opline->opcode != ZEND_CASE
&& opline->opcode != ZEND_CASE_STRICT
&& opline->opcode != ZEND_COPY_TMP) {
free_var = ssa_op->op1_use;
free_var_type = opline->op1_type;
}
Expand Down
10 changes: 10 additions & 0 deletions Zend/Optimizer/pass1.c
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,16 @@ void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx)
collect_constants = 0;
break;
}
case ZEND_DO_UCALL:
case ZEND_DO_FCALL:
case ZEND_DO_FCALL_BY_NAME:
case ZEND_FRAMELESS_ICALL_0:
case ZEND_FRAMELESS_ICALL_1:
case ZEND_FRAMELESS_ICALL_2:
case ZEND_FRAMELESS_ICALL_3:
/* don't collect constants after any UCALL/FCALL/FRAMELESS ICALL */
collect_constants = 0;
break;
case ZEND_STRLEN:
if (opline->op1_type == IS_CONST &&
zend_optimizer_eval_strlen(&result, &ZEND_OP1_LITERAL(opline)) == SUCCESS) {
Expand Down
48 changes: 48 additions & 0 deletions Zend/zend_compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include "zend_API.h"
#include "zend_exceptions.h"
#include "zend_interfaces.h"
#include "zend_types.h"
#include "zend_virtual_cwd.h"
#include "zend_multibyte.h"
#include "zend_language_scanner.h"
Expand Down Expand Up @@ -4954,6 +4955,51 @@ static zend_result zend_compile_func_sprintf(znode *result, zend_ast_list *args)
return SUCCESS;
}

static zend_result zend_compile_func_printf(znode *result, zend_ast_list *args) /* {{{ */
{
/* Special case: printf with a single constant string argument and no format specifiers.
* In this case, just emit ECHO and return the string length if needed. */
if (args->children == 1) {
zend_eval_const_expr(&args->child[0]);
if (args->child[0]->kind != ZEND_AST_ZVAL) {
return FAILURE;
}
zval *format_string = zend_ast_get_zval(args->child[0]);
if (Z_TYPE_P(format_string) != IS_STRING) {
return FAILURE;
}
/* Check if there are any format specifiers */
if (!memchr(Z_STRVAL_P(format_string), '%', Z_STRLEN_P(format_string))) {
/* No format specifiers - just emit ECHO and return string length */
znode format_node;
zend_compile_expr(&format_node, args->child[0]);
zend_emit_op(NULL, ZEND_ECHO, &format_node, NULL);

/* Return the string length as a constant if the result is used */
result->op_type = IS_CONST;
ZVAL_LONG(&result->u.constant, Z_STRLEN_P(format_string));
return SUCCESS;
}
}

/* Fall back to sprintf optimization for format strings with specifiers */
znode rope_result;
if (zend_compile_func_sprintf(&rope_result, args) != SUCCESS) {
return FAILURE;
}

/* printf() returns the amount of bytes written, so just an ECHO of the resulting sprintf()
* optimisation might not be enough. At this early stage we can't detect if the result is
* actually used, so we just emit the opcodes and cleanup if they are not used in the
* optimizers block pass later. */
znode copy;
zend_emit_op_tmp(&copy, ZEND_COPY_TMP, &rope_result, NULL);
zend_emit_op(NULL, ZEND_ECHO, &rope_result, NULL);
zend_emit_op_tmp(result, ZEND_STRLEN, &copy, NULL);

return SUCCESS;
}

static zend_result zend_compile_func_clone(znode *result, zend_ast_list *args)
{
znode arg_node;
Expand Down Expand Up @@ -5036,6 +5082,8 @@ static zend_result zend_try_compile_special_func_ex(znode *result, zend_string *
return zend_compile_func_array_key_exists(result, args);
} else if (zend_string_equals_literal(lcname, "sprintf")) {
return zend_compile_func_sprintf(result, args);
} else if (zend_string_equals_literal(lcname, "printf")) {
return zend_compile_func_printf(result, args);
} else if (zend_string_equals(lcname, ZSTR_KNOWN(ZEND_STR_CLONE))) {
return zend_compile_func_clone(result, args);
} else {
Expand Down