@@ -466,10 +466,7 @@ int zend_dfa_optimize_calls(zend_op_array *op_array, zend_ssa *ssa)
466466 int var_num = ssa_op -> op1_use ;
467467 zend_ssa_var * var = ssa -> vars + var_num ;
468468
469- if (ssa_op -> op1_def >= 0 ) {
470- zend_ssa_replace_op1_def_op1_use (ssa , ssa_op );
471- }
472-
469+ ZEND_ASSERT (ssa_op -> op1_def < 0 );
473470 zend_ssa_unlink_use_chain (ssa , op_num , ssa_op -> op1_use );
474471 ssa_op -> op1_use = -1 ;
475472 ssa_op -> op1_use_chain = -1 ;
@@ -1069,50 +1066,73 @@ static bool zend_dfa_try_to_replace_result(zend_op_array *op_array, zend_ssa *ss
10691066 return 0 ;
10701067}
10711068
1069+ static bool op_dominates (const zend_op_array * op_array , const zend_ssa * ssa , const zend_op * a , const zend_op * b )
1070+ {
1071+ uint32_t a_block = ssa -> cfg .map [a - op_array -> opcodes ];
1072+ uint32_t b_block = ssa -> cfg .map [b - op_array -> opcodes ];
1073+ if (a_block == b_block ) {
1074+ return a < b ;
1075+ } else {
1076+ return dominates (ssa -> cfg .blocks , a_block , b_block );
1077+ }
1078+ }
1079+
1080+ static bool op_dominates_all_uses (const zend_op_array * op_array , const zend_ssa * ssa , int start ) {
1081+ int use ;
1082+ FOREACH_USE (ssa -> vars + ssa -> ops [start ].op1_def , use ) {
1083+ if (!op_dominates (op_array , ssa , op_array -> opcodes + start , op_array -> opcodes + use )) {
1084+ return false;
1085+ }
1086+ } FOREACH_USE_END ();
1087+ return true;
1088+ }
1089+
10721090/* Sets a flag on SEND ops when a copy can be a avoided. */
1073- static void zend_dfa_optimize_send_copies (zend_op_array * op_array , zend_ssa * ssa )
1091+ static void zend_dfa_optimize_send_copies (zend_op_array * op_array , const zend_ssa * ssa )
10741092{
1075- /* func_get_args() and indirect accesses could make the optimization observable */
1076- if (ssa -> cfg .flags & ( ZEND_FUNC_VARARG | ZEND_FUNC_INDIRECT_VAR_ACCESS ) ) {
1093+ /* func_get_args() etc could make the optimization observable */
1094+ if (ssa -> cfg .flags & ZEND_FUNC_VARARG ) {
10771095 return ;
10781096 }
10791097
10801098 for (uint32_t i = 0 ; i < op_array -> last ; i ++ ) {
1081- zend_op * opline = op_array -> opcodes + i ;
1099+ const zend_op * opline = & op_array -> opcodes [ i ] ;
10821100 if ((opline -> opcode != ZEND_SEND_VAR && opline -> opcode != ZEND_SEND_VAR_EX ) || opline -> op2_type != IS_UNUSED || opline -> op1_type != IS_CV ) {
10831101 continue ;
10841102 }
1085-
1086- zend_ssa_op * ssa_op = ssa -> ops + i ;
1087- int op1_def = ssa_op -> op1_def ;
1088- if (op1_def == -1 ) {
1089- continue ;
1090- }
1091-
1092- int ssa_cv = ssa_op -> op1_use ;
1093-
1103+ int ssa_cv = ssa -> ops [i ].op1_use ;
10941104#if 0
1095- /* Argument move must not be observable in backtraces */
1105+ /* NULL must not be visible in backtraces */
10961106 if (ssa -> vars [ssa_cv ].var < op_array -> num_args ) {
10971107 continue ;
10981108 }
10991109#endif
1100-
11011110 /* Unsetting a CV is always fine if it gets overwritten afterwards.
11021111 * Since type inference often infers very wide types, we are very loose in matching types. */
11031112 uint32_t type = ssa -> var_info [ssa_cv ].type ;
1104- if ((type & (MAY_BE_REF /* |MAY_BE_UNDEF*/ )) || /* !(type & MAY_BE_RC1) || */ !(type & (MAY_BE_STRING |MAY_BE_ARRAY ))) {
1113+ if ((type & (MAY_BE_REF |MAY_BE_UNDEF )) || !(type & MAY_BE_RC1 ) || !(type & (MAY_BE_STRING |MAY_BE_ARRAY ))) {
11051114 continue ;
11061115 }
11071116
1108- zend_ssa_var * ssa_var = ssa -> vars + op1_def ;
1109-
1110- if (ssa_var -> no_val && !ssa_var -> alias ) {
1111- /* Flag will be used by VM type spec handler */
1112- opline -> extended_value = 1 ;
1113- // fprintf(stderr, "optimize\n");
1114- } else if (opline -> opcode == ZEND_SEND_VAR ) {
1115- //zend_ssa_replace_op1_def_op1_use(ssa, ssa_op);
1117+ if (false&& opline -> opcode == ZEND_SEND_VAR ) {
1118+ /* Check if the call dominates the assignment and the assignment dominates all the future uses of this SSA variable */
1119+ int next_use = ssa -> ops [i ].op1_use_chain ;
1120+ if (next_use >= 0
1121+ && op_array -> opcodes [next_use ].opcode == ZEND_ASSIGN
1122+ && ssa -> ops [next_use ].op1_use == ssa_cv
1123+ && ssa -> ops [next_use ].op2_use >= 0
1124+ && op_dominates (op_array , ssa , opline , op_array -> opcodes + next_use )) {
1125+ if (op_dominates_all_uses (op_array , ssa , next_use )) {
1126+ op_array -> opcodes [i ].extended_value = 1 ;
1127+ //fprintf(stderr, "yes optimize 1\n");
1128+ }
1129+ }
1130+ } else if (opline -> opcode == ZEND_SEND_VAR_EX ) /* ZEND_SEND_VAR_EX */ {
1131+ ZEND_ASSERT (ssa -> ops [i ].op1_def != -1 );
1132+ if (ssa -> vars [ssa -> ops [i ].op1_def ].no_val ) {
1133+ op_array -> opcodes [i ].extended_value = 1 ;
1134+ //fprintf(stderr, "yes optimize 2\n");
1135+ }
11161136 }
11171137 }
11181138}
@@ -1178,9 +1198,6 @@ void zend_dfa_optimize_op_array(zend_op_array *op_array, zend_optimizer_ctx *ctx
11781198 /* Optimization should not be done on main because of globals. */
11791199 if (op_array -> function_name ) {
11801200 zend_dfa_optimize_send_copies (op_array , ssa );
1181- #if ZEND_DEBUG_DFA
1182- ssa_verify_integrity (op_array , ssa , "after optimize send copies" );
1183- #endif
11841201 }
11851202
11861203 for (v = op_array -> last_var ; v < ssa -> vars_count ; v ++ ) {
0 commit comments