From: Dmitry Stogov Date: Mon, 19 Feb 2018 09:21:43 +0000 (+0300) Subject: Allow optional ignorance of operator overlaoding. Optimizer takes into account possib... X-Git-Tag: php-7.3.0alpha1~347 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=a02a126d54077d1f2f3c9a54a00a7e75d5b8f562;p=php Allow optional ignorance of operator overlaoding. Optimizer takes into account possibility of operator overloading at default optimization level, but ignores this possibility at "unsafe" level (opcache.optimization_level=-1). --- diff --git a/ext/opcache/Optimizer/dfa_pass.c b/ext/opcache/Optimizer/dfa_pass.c index ae067c58eb..1f62b9ec93 100644 --- a/ext/opcache/Optimizer/dfa_pass.c +++ b/ext/opcache/Optimizer/dfa_pass.c @@ -110,7 +110,7 @@ int zend_dfa_analyze_op_array(zend_op_array *op_array, zend_optimizer_ctx *ctx, return FAILURE; } - if (zend_ssa_inference(&ctx->arena, op_array, ctx->script, ssa) != SUCCESS) { + if (zend_ssa_inference(&ctx->arena, op_array, ctx->script, ssa, ctx->optimization_level) != SUCCESS) { return FAILURE; } diff --git a/ext/opcache/Optimizer/zend_inference.c b/ext/opcache/Optimizer/zend_inference.c index a0302132d2..08ae51667e 100644 --- a/ext/opcache/Optimizer/zend_inference.c +++ b/ext/opcache/Optimizer/zend_inference.c @@ -2092,16 +2092,19 @@ static uint32_t assign_dim_result_type( /* For binary ops that have compound assignment operators */ static uint32_t binary_op_result_type( - zend_ssa *ssa, zend_uchar opcode, uint32_t t1, uint32_t t2, uint32_t result_var) { + zend_ssa *ssa, zend_uchar opcode, uint32_t t1, uint32_t t2, uint32_t result_var, + zend_long optimization_level) { uint32_t tmp = 0; uint32_t t1_type = (t1 & MAY_BE_ANY) | (t1 & MAY_BE_UNDEF ? MAY_BE_NULL : 0); uint32_t t2_type = (t2 & MAY_BE_ANY) | (t2 & MAY_BE_UNDEF ? MAY_BE_NULL : 0); - /* Handle potentially overloaded operators. - * This could be made more precise by checking the class type, if known. */ - if ((t1_type & MAY_BE_OBJECT) || (t2_type & MAY_BE_OBJECT)) { - /* This is somewhat GMP specific. */ - tmp |= MAY_BE_OBJECT | MAY_BE_FALSE | MAY_BE_RC1; + if (!(ZEND_OPTIMIZER_IGNORE_OVERLOADING & optimization_level)) { + /* Handle potentially overloaded operators. + * This could be made more precise by checking the class type, if known. */ + if ((t1_type & MAY_BE_OBJECT) || (t2_type & MAY_BE_OBJECT)) { + /* This is somewhat GMP specific. */ + tmp |= MAY_BE_OBJECT | MAY_BE_FALSE | MAY_BE_RC1; + } } switch (opcode) { @@ -2158,7 +2161,11 @@ static uint32_t binary_op_result_type( * handling */ break; case ZEND_MOD: - tmp |= MAY_BE_LONG; + if (ZEND_OPTIMIZER_IGNORE_OVERLOADING & optimization_level) { + tmp = MAY_BE_LONG; + } else { + tmp |= MAY_BE_LONG; + } /* Division by zero results in an exception, so it doesn't need any special handling */ break; case ZEND_BW_OR: @@ -2173,7 +2180,11 @@ static uint32_t binary_op_result_type( break; case ZEND_SL: case ZEND_SR: - tmp |= MAY_BE_LONG; + if (ZEND_OPTIMIZER_IGNORE_OVERLOADING & optimization_level) { + tmp = MAY_BE_LONG; + } else { + tmp |= MAY_BE_LONG; + } break; case ZEND_CONCAT: case ZEND_FAST_CONCAT: @@ -2240,7 +2251,8 @@ static int zend_update_type_info(const zend_op_array *op_array, zend_ssa *ssa, const zend_script *script, zend_bitset worklist, - int i) + int i, + zend_long optimization_level) { uint32_t t1, t2; uint32_t tmp, orig; @@ -2290,7 +2302,7 @@ static int zend_update_type_info(const zend_op_array *op_array, case ZEND_SL: case ZEND_SR: case ZEND_CONCAT: - tmp = binary_op_result_type(ssa, opline->opcode, t1, t2, ssa_ops[i].result_def); + tmp = binary_op_result_type(ssa, opline->opcode, t1, t2, ssa_ops[i].result_def, optimization_level); UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def); break; case ZEND_BW_NOT: @@ -2301,9 +2313,11 @@ static int zend_update_type_info(const zend_op_array *op_array, if (t1 & (MAY_BE_ANY-MAY_BE_STRING)) { tmp |= MAY_BE_LONG; } - if (t1 & MAY_BE_OBJECT) { - /* Potentially overloaded operator. */ - tmp |= MAY_BE_OBJECT | MAY_BE_RC1; + if (!(ZEND_OPTIMIZER_IGNORE_OVERLOADING & optimization_level)) { + if (t1 & MAY_BE_OBJECT) { + /* Potentially overloaded operator. */ + tmp |= MAY_BE_OBJECT | MAY_BE_RC1; + } } UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def); break; @@ -2445,7 +2459,7 @@ static int zend_update_type_info(const zend_op_array *op_array, } tmp |= binary_op_result_type( - ssa, get_compound_assign_op(opline->opcode), t1, t2, ssa_ops[i].op1_def); + ssa, get_compound_assign_op(opline->opcode), t1, t2, ssa_ops[i].op1_def, optimization_level); if (tmp & (MAY_BE_STRING|MAY_BE_ARRAY)) { tmp |= MAY_BE_RC1; } @@ -3468,7 +3482,7 @@ static zend_class_entry *join_class_entries( return ce1; } -int zend_infer_types_ex(const zend_op_array *op_array, const zend_script *script, zend_ssa *ssa, zend_bitset worklist) +int zend_infer_types_ex(const zend_op_array *op_array, const zend_script *script, zend_ssa *ssa, zend_bitset worklist, zend_long optimization_level) { zend_basic_block *blocks = ssa->cfg.blocks; zend_ssa_var *ssa_vars = ssa->vars; @@ -3535,7 +3549,7 @@ int zend_infer_types_ex(const zend_op_array *op_array, const zend_script *script } } else if (ssa_vars[j].definition >= 0) { i = ssa_vars[j].definition; - if (zend_update_type_info(op_array, ssa, script, worklist, i) == FAILURE) { + if (zend_update_type_info(op_array, ssa, script, worklist, i, optimization_level) == FAILURE) { return FAILURE; } } @@ -3707,7 +3721,7 @@ static zend_bool can_convert_to_double( return 1; } -static int zend_type_narrowing(const zend_op_array *op_array, const zend_script *script, zend_ssa *ssa) +static int zend_type_narrowing(const zend_op_array *op_array, const zend_script *script, zend_ssa *ssa, zend_long optimization_level) { uint32_t bitset_len = zend_bitset_len(ssa->vars_count); zend_bitset visited, worklist; @@ -3751,7 +3765,7 @@ static int zend_type_narrowing(const zend_op_array *op_array, const zend_script return SUCCESS; } - if (zend_infer_types_ex(op_array, script, ssa, worklist) != SUCCESS) { + if (zend_infer_types_ex(op_array, script, ssa, worklist, optimization_level) != SUCCESS) { free_alloca(visited, use_heap); return FAILURE; } @@ -4001,7 +4015,7 @@ void zend_func_return_info(const zend_op_array *op_array, ret->has_range = tmp_has_range; } -static int zend_infer_types(const zend_op_array *op_array, const zend_script *script, zend_ssa *ssa) +static int zend_infer_types(const zend_op_array *op_array, const zend_script *script, zend_ssa *ssa, zend_long optimization_level) { zend_ssa_var_info *ssa_var_info = ssa->var_info; int ssa_vars_count = ssa->vars_count; @@ -4018,13 +4032,13 @@ static int zend_infer_types(const zend_op_array *op_array, const zend_script *sc ssa_var_info[j].type = 0; } - if (zend_infer_types_ex(op_array, script, ssa, worklist) != SUCCESS) { + if (zend_infer_types_ex(op_array, script, ssa, worklist, optimization_level) != SUCCESS) { free_alloca(worklist, use_heap); return FAILURE; } /* Narrowing integer initialization to doubles */ - zend_type_narrowing(op_array, script, ssa); + zend_type_narrowing(op_array, script, ssa, optimization_level); if (ZEND_FUNC_INFO(op_array)) { zend_func_return_info(op_array, script, 1, 0, &ZEND_FUNC_INFO(op_array)->return_info); @@ -4034,7 +4048,7 @@ static int zend_infer_types(const zend_op_array *op_array, const zend_script *sc return SUCCESS; } -int zend_ssa_inference(zend_arena **arena, const zend_op_array *op_array, const zend_script *script, zend_ssa *ssa) /* {{{ */ +int zend_ssa_inference(zend_arena **arena, const zend_op_array *op_array, const zend_script *script, zend_ssa *ssa, zend_long optimization_level) /* {{{ */ { zend_ssa_var_info *ssa_var_info; int i; @@ -4067,7 +4081,7 @@ int zend_ssa_inference(zend_arena **arena, const zend_op_array *op_array, const return FAILURE; } - if (zend_infer_types(op_array, script, ssa) != SUCCESS) { + if (zend_infer_types(op_array, script, ssa, optimization_level) != SUCCESS) { return FAILURE; } diff --git a/ext/opcache/Optimizer/zend_inference.h b/ext/opcache/Optimizer/zend_inference.h index 7b058fe45d..e3d767da04 100644 --- a/ext/opcache/Optimizer/zend_inference.h +++ b/ext/opcache/Optimizer/zend_inference.h @@ -243,7 +243,7 @@ BEGIN_EXTERN_C() int zend_ssa_find_false_dependencies(const zend_op_array *op_array, zend_ssa *ssa); int zend_ssa_find_sccs(const zend_op_array *op_array, zend_ssa *ssa); -int zend_ssa_inference(zend_arena **raena, const zend_op_array *op_array, const zend_script *script, zend_ssa *ssa); +int zend_ssa_inference(zend_arena **raena, const zend_op_array *op_array, const zend_script *script, zend_ssa *ssa, zend_long optimization_level); uint32_t zend_array_element_type(uint32_t t1, int write, int insert); @@ -253,7 +253,7 @@ int zend_inference_narrowing_meet(zend_ssa_var_info *var_info, zend_ssa_range * int zend_inference_widening_meet(zend_ssa_var_info *var_info, zend_ssa_range *r); void zend_inference_check_recursive_dependencies(zend_op_array *op_array); -int zend_infer_types_ex(const zend_op_array *op_array, const zend_script *script, zend_ssa *ssa, zend_bitset worklist); +int zend_infer_types_ex(const zend_op_array *op_array, const zend_script *script, zend_ssa *ssa, zend_bitset worklist, zend_long optimization_level); void zend_init_func_return_info(const zend_op_array *op_array, const zend_script *script, diff --git a/ext/opcache/Optimizer/zend_optimizer.h b/ext/opcache/Optimizer/zend_optimizer.h index e1c96bec45..3c6a0fe50e 100644 --- a/ext/opcache/Optimizer/zend_optimizer.h +++ b/ext/opcache/Optimizer/zend_optimizer.h @@ -39,12 +39,14 @@ #define ZEND_OPTIMIZER_PASS_12 (1<<11) /* Adjust used stack */ #define ZEND_OPTIMIZER_PASS_13 (1<<12) /* Remove unused variables */ #define ZEND_OPTIMIZER_PASS_14 (1<<13) /* DCE (dead code elimination) */ -#define ZEND_OPTIMIZER_PASS_15 (1<<14) /* Collect constants */ +#define ZEND_OPTIMIZER_PASS_15 (1<<14) /* (unsafe) Collect constants */ #define ZEND_OPTIMIZER_PASS_16 (1<<15) /* Inline functions */ +#define ZEND_OPTIMIZER_IGNORE_OVERLOADING (1<<16) /* (unsafe) Ignore possibility of operator overloading */ + #define ZEND_OPTIMIZER_ALL_PASSES 0x7FFFFFFF -#define DEFAULT_OPTIMIZATION_LEVEL "0x7FFFBFFF" +#define DEFAULT_OPTIMIZATION_LEVEL "0x7FFEBFFF" #define ZEND_DUMP_AFTER_PASS_1 ZEND_OPTIMIZER_PASS_1