@@ -466,7 +466,10 @@ 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- ZEND_ASSERT (ssa_op -> op1_def < 0 );
469+ if (ssa_op -> op1_def >= 0 ) {
470+ zend_ssa_replace_op1_def_op1_use (ssa , ssa_op );
471+ }
472+
470473 zend_ssa_unlink_use_chain (ssa , op_num , ssa_op -> op1_use );
471474 ssa_op -> op1_use = -1 ;
472475 ssa_op -> op1_use_chain = -1 ;
@@ -1066,46 +1069,35 @@ static bool zend_dfa_try_to_replace_result(zend_op_array *op_array, zend_ssa *ss
10661069 return 0 ;
10671070}
10681071
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-
10901072/* Sets a flag on SEND ops when a copy can be a avoided. */
1091- static void zend_dfa_optimize_send_copies (zend_op_array * op_array , const zend_ssa * ssa )
1073+ static void zend_dfa_optimize_send_copies (zend_op_array * op_array , zend_ssa * ssa )
10921074{
1093- /* func_get_args() etc could make the optimization observable */
1075+ /* func_get_args(), indirect accesses and exceptions could make the optimization observable.
1076+ * The latter two cases are already tested before applying the DFA pass. */
10941077 if (ssa -> cfg .flags & ZEND_FUNC_VARARG ) {
10951078 return ;
10961079 }
10971080
10981081 for (uint32_t i = 0 ; i < op_array -> last ; i ++ ) {
1099- const zend_op * opline = & op_array -> opcodes [ i ] ;
1082+ zend_op * opline = op_array -> opcodes + i ;
11001083 if ((opline -> opcode != ZEND_SEND_VAR && opline -> opcode != ZEND_SEND_VAR_EX ) || opline -> op2_type != IS_UNUSED || opline -> op1_type != IS_CV ) {
11011084 continue ;
11021085 }
11031086
1104- /* NULL must not be visible in backtraces */
1105- int ssa_cv = ssa -> ops [i ].op1_use ;
1087+ zend_ssa_op * ssa_op = ssa -> ops + i ;
1088+ int op1_def = ssa_op -> op1_def ;
1089+ if (op1_def == -1 ) {
1090+ continue ;
1091+ }
1092+
1093+ int ssa_cv = ssa_op -> op1_use ;
1094+
1095+ #if 0
1096+ /* Argument move must not be observable in backtraces */
11061097 if (ssa -> vars [ssa_cv ].var < op_array -> num_args ) {
11071098 continue ;
11081099 }
1100+ #endif
11091101
11101102 /* Unsetting a CV is always fine if it gets overwritten afterwards.
11111103 * Since type inference often infers very wide types, we are very loose in matching types. */
@@ -1114,25 +1106,11 @@ static void zend_dfa_optimize_send_copies(zend_op_array *op_array, const zend_ss
11141106 continue ;
11151107 }
11161108
1117- if (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 /* 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- }
1109+ zend_ssa_var * ssa_var = ssa -> vars + op1_def ;
1110+
1111+ if (ssa_var -> no_val && !ssa_var -> alias ) {
1112+ /* Flag will be used by VM type spec handler */
1113+ opline -> extended_value = 1 ;
11361114 }
11371115 }
11381116}
@@ -1198,6 +1176,9 @@ void zend_dfa_optimize_op_array(zend_op_array *op_array, zend_optimizer_ctx *ctx
11981176 /* Optimization should not be done on main because of globals. */
11991177 if (op_array -> function_name ) {
12001178 zend_dfa_optimize_send_copies (op_array , ssa );
1179+ #if ZEND_DEBUG_DFA
1180+ ssa_verify_integrity (op_array , ssa , "after optimize send copies" );
1181+ #endif
12011182 }
12021183
12031184 for (v = op_array -> last_var ; v < ssa -> vars_count ; v ++ ) {
0 commit comments