@ -842,40 +842,63 @@ int pass_two_wrapper(zval *el)
return pass_two ( ( zend_op_array * ) Z_PTR_P ( el ) ) ;
}
typedef struct _var_live_info {
struct _var_live_info * next ;
uint32_t start ;
uint32_t end ;
} var_live_info ;
/* The following liveliness analyzing algorithm assumes that
* 1 ) temporary variables are defined before use
* 2 ) they have linear live - ranges without " holes "
* 3 ) Opcodes never use and define the same temorary variables
*/
typedef struct _op_var_info {
struct _op_var_info * next ;
uint32_t var ;
} op_var_info ;
/* The following liveliness analyzing algorithm assumes that
* 1 ) temporary variables are defined before use
* 2 ) they have linear live - ranges without " holes "
*/
static zend_always_inline uint32_t liveliness_kill_var ( zend_op_array * op_array , zend_op * cur_op , uint32_t var , uint32_t * Tstart , op_var_info * * opTs )
{
uint32_t start = Tstart [ var ] ;
uint32_t end = cur_op - op_array - > opcodes ;
uint32_t count = 0 ;
uint32_t var_offset , j ;
Tstart [ var ] = - 1 ;
if ( cur_op - > opcode = = ZEND_OP_DATA ) {
end - - ;
}
start + + ;
if ( op_array - > opcodes [ start ] . opcode = = ZEND_OP_DATA
| | op_array - > opcodes [ start ] . opcode = = ZEND_FE_FETCH_R
| | op_array - > opcodes [ start ] . opcode = = ZEND_FE_FETCH_RW ) {
start + + ;
}
if ( start < end ) {
var_offset = ( uint32_t ) ( zend_intptr_t ) ZEND_CALL_VAR_NUM ( NULL , op_array - > last_var + var ) ;
if ( op_array - > opcodes [ end ] . opcode = = ZEND_ROPE_END ) {
var_offset | = ZEND_LIVE_ROPE ;
} else if ( op_array - > opcodes [ end ] . opcode = = ZEND_END_SILENCE ) {
var_offset | = ZEND_LIVE_SILENCE ;
} else if ( op_array - > opcodes [ end ] . opcode = = ZEND_FE_FREE ) {
var_offset | = ZEND_LIVE_LOOP ;
}
for ( j = start ; j < end ; j + + ) {
op_var_info * newOpTs = zend_arena_alloc ( & CG ( arena ) , sizeof ( op_var_info ) ) ;
newOpTs - > next = opTs [ j ] ;
newOpTs - > var = var_offset ;
opTs [ j ] = newOpTs ;
count + + ;
}
}
return count ;
}
ZEND_API uint32_t * generate_var_liveliness_info ( zend_op_array * op_array )
{
uint32_t i , op_live_total = 0 ;
void * checkpoint = zend_arena_checkpoint ( CG ( arena ) ) ;
uint32_t * info , info_off = op_array - > last + 1 ;
var_live_info * * Ts = zend_arena_alloc ( & CG ( arena ) , sizeof ( var_live_info * ) * op_array - > T ) ;
uint32_t * Tstart = zend_arena_alloc ( & CG ( arena ) , sizeof ( uint32_t ) * op_array - > T ) ;
op_var_info * * opTs = zend_arena_alloc ( & CG ( arena ) , sizeof ( op_var_info * ) * ( op_array - > last + 1 ) ) ;
for ( i = 0 ; i < op_array - > T ; i + + ) {
Ts [ i ] = zend_arena_alloc ( & CG ( arena ) , sizeof ( var_live_info ) ) ;
Ts [ i ] - > next = NULL ;
Ts [ i ] - > start = Ts [ i ] - > end = - 1 ;
}
for ( i = 0 ; i < = op_array - > last ; i + + ) {
opTs [ i ] = zend_arena_alloc ( & CG ( arena ) , sizeof ( op_var_info ) ) ;
opTs [ i ] - > var = - 1 ;
opTs [ i ] - > next = NULL ;
}
memset ( Tstart , - 1 , sizeof ( uint32_t ) * op_array - > T ) ;
memset ( opTs , 0 , sizeof ( op_var_info * ) * ( op_array - > last + 1 ) ) ;
zend_op * end_op = op_array - > opcodes + op_array - > last ;
zend_op * cur_op = op_array - > opcodes ;
@ -885,112 +908,61 @@ ZEND_API uint32_t *generate_var_liveliness_info(zend_op_array *op_array)
& & cur_op - > opcode ! = ZEND_BOOL /* the following opcodes are used in inline branching (and anyway always bool, so no need to free) and may not be defined depending on the taken branch */
& & cur_op - > opcode ! = ZEND_JMPZ_EX
& & cur_op - > opcode ! = ZEND_JMPNZ_EX
& & ( cur_op - > opcode ! = ZEND_QM_ASSIGN | | ( cur_op + 1 ) - > opcode ! = ZEND_JMP ) ) { /* these two consecutive ops appear on ternary; the result of true branch is undefined for false branch */
var_live_info * T = Ts [ cur_op - > result . var ] ;
if ( T - > end ! = ( uint32_t ) - 1 ) {
Ts [ cur_op - > result . var ] = zend_arena_alloc ( & CG ( arena ) , sizeof ( var_live_info ) ) ;
Ts [ cur_op - > result . var ] - > next = T ;
T = Ts [ cur_op - > result . var ] ;
T - > start = T - > end = - 1 ;
}
if ( T - > start = = - 1 /* we need the exceptions *here* and not around the whole branch, else temporary ranges may be accidentally reused. If any of these opcodes reuses a temporary, we must mark the previous range as definitely terminated first */
& & cur_op - > opcode ! = ZEND_CASE /* exception for opcache, it might nowhere use the temporary (anyway bool, so no need to free) */
& & cur_op - > opcode ! = ZEND_ROPE_ADD /* the following opcodes reuse TMP created before */
& & cur_op - > opcode ! = ZEND_ADD_ARRAY_ELEMENT
& & cur_op - > opcode ! = ZEND_FAST_CALL /* passes fast_call */
& & cur_op - > opcode ! = ZEND_FETCH_CLASS /* the following opcodes pass class_entry */
& & cur_op - > opcode ! = ZEND_DECLARE_CLASS
& & cur_op - > opcode ! = ZEND_DECLARE_INHERITED_CLASS
& & cur_op - > opcode ! = ZEND_DECLARE_INHERITED_CLASS_DELAYED
& & cur_op - > opcode ! = ZEND_DECLARE_ANON_CLASS
& & cur_op - > opcode ! = ZEND_DECLARE_ANON_INHERITED_CLASS ) {
/* Objects created via ZEND_NEW are only fully initialized after the DO_FCALL (constructor call) */
if ( cur_op - > opcode = = ZEND_NEW ) {
T - > start = cur_op - > op2 . opline_num - 1 ;
} else {
T - > start = cur_op - op_array - > opcodes ;
}
& & ( cur_op - > opcode ! = ZEND_QM_ASSIGN | | ( cur_op + 1 ) - > opcode ! = ZEND_JMP ) /* these two consecutive ops appear on ternary; the result of true branch is undefined for false branch */
& & cur_op - > opcode ! = ZEND_CASE /* exception for opcache, it might nowhere use the temporary (anyway bool, so no need to free) */
& & cur_op - > opcode ! = ZEND_ROPE_ADD /* the following opcodes reuse TMP created before */
& & cur_op - > opcode ! = ZEND_ADD_ARRAY_ELEMENT
& & cur_op - > opcode ! = ZEND_FAST_CALL /* passes fast_call */
& & cur_op - > opcode ! = ZEND_FETCH_CLASS /* the following opcodes pass class_entry */
& & cur_op - > opcode ! = ZEND_DECLARE_CLASS
& & cur_op - > opcode ! = ZEND_DECLARE_INHERITED_CLASS
& & cur_op - > opcode ! = ZEND_DECLARE_INHERITED_CLASS_DELAYED
& & cur_op - > opcode ! = ZEND_DECLARE_ANON_CLASS
& & cur_op - > opcode ! = ZEND_DECLARE_ANON_INHERITED_CLASS ) {
/* Objects created via ZEND_NEW are only fully initialized after the DO_FCALL (constructor call) */
if ( cur_op - > opcode = = ZEND_NEW ) {
Tstart [ cur_op - > result . var ] = cur_op - > op2 . opline_num - 1 ;
} else {
Tstart [ cur_op - > result . var ] = cur_op - op_array - > opcodes ;
}
}
if ( ( cur_op - > op1_type & ( IS_VAR | IS_TMP_VAR ) )
& & cur_op - > opcode ! = ZEND_ROPE_ADD ) { /* the following opcodes don't free TMP */
var_live_info * T = Ts [ cur_op - > op1 . var ] ;
if ( T - > start ! = ( uint32_t ) - 1 ) {
T - > end = cur_op - op_array - > opcodes ;
if ( cur_op - > opcode = = ZEND_OP_DATA ) {
T - > end - - ;
}
}
& & Tstart [ cur_op - > op1 . var ] ! = ( uint32_t ) - 1
& & cur_op - > opcode ! = ZEND_ROPE_ADD /* the following opcodes don't free TMP */
& & cur_op - > opcode ! = ZEND_FETCH_LIST
& & cur_op - > opcode ! = ZEND_CASE
& & cur_op - > opcode ! = ZEND_FE_FETCH_R
& & cur_op - > opcode ! = ZEND_FE_FETCH_RW ) {
op_live_total + = liveliness_kill_var ( op_array , cur_op , cur_op - > op1 . var , Tstart , opTs ) ;
}
if ( cur_op - > op2_type & ( IS_VAR | IS_TMP_VAR ) ) {
var_live_info * T = Ts [ cur_op - > op2 . var ] ;
if ( T - > start ! = ( uint32_t ) - 1 ) {
T - > end = cur_op - op_array - > opcodes ;
if ( cur_op - > opcode = = ZEND_OP_DATA ) {
T - > end - - ;
}
}
if ( cur_op - > op2_type & ( IS_VAR | IS_TMP_VAR )
& & Tstart [ cur_op - > op2 . var ] ! = ( uint32_t ) - 1 ) {
op_live_total + = liveliness_kill_var ( op_array , cur_op , cur_op - > op2 . var , Tstart , opTs ) ;
}
}
# if ZEND_DEBUG
/* Check that all TMP variable live-ranges are closed */
for ( i = 0 ; i < op_array - > T ; i + + ) {
int j ;
var_live_info * T = Ts [ i ] ;
do {
if ( T - > start = = ( uint32_t ) - 1 ) {
continue ;
}
/* if one lands here, some opcode range isn't properly terminated or part of the exceptions */
ZEND_ASSERT ( T - > end ! = ( uint32_t ) - 1 ) ;
j = T - > start + 1 ;
if ( op_array - > opcodes [ j ] . opcode = = ZEND_OP_DATA
| | op_array - > opcodes [ j ] . opcode = = ZEND_FE_FETCH_R
| | op_array - > opcodes [ j ] . opcode = = ZEND_FE_FETCH_RW ) {
/* On exception TMP variable is destroyed by current opcode */
j + + ;
}
for ( ; j < T - > end ; j + + ) {
op_var_info * opT = opTs [ j ] ;
if ( opT - > var ! = ( uint32_t ) - 1 ) {
opTs [ j ] = zend_arena_alloc ( & CG ( arena ) , sizeof ( op_var_info ) ) ;
opTs [ j ] - > next = opT ;
opT = opTs [ j ] ;
}
opT - > var = ( uint32_t ) ( zend_intptr_t ) ZEND_CALL_VAR_NUM ( NULL , op_array - > last_var + i ) ; ;
if ( op_array - > opcodes [ T - > end ] . opcode = = ZEND_ROPE_END ) {
opT - > var | = ZEND_LIVE_ROPE ;
} else if ( op_array - > opcodes [ T - > end ] . opcode = = ZEND_END_SILENCE ) {
opT - > var | = ZEND_LIVE_SILENCE ;
} else if ( op_array - > opcodes [ T - > end ] . opcode = = ZEND_FE_FREE ) {
opT - > var | = ZEND_LIVE_LOOP ;
}
op_live_total + + ;
}
} while ( ( T = T - > next ) ) ;
ZEND_ASSERT ( Tstart [ i ] = = ( uint32_t ) - 1 ) ;
}
# endif
if ( ! op_live_total ) {
zend_arena_release ( & CG ( arena ) , checkpoint ) ;
return NULL ;
}
info = emalloc ( op_live_total * sizeof ( uint32_t ) + ( op_array - > last + 1 ) * sizeof ( uint32_t ) ) ;
for ( i = 0 ; i < op_array - > last ; i + + ) {
op_var_info * opT = opTs [ i ] ;
info [ i ] = info_off ;
if ( opT - > var = = ( uint32_t ) - 1 ) {
continue ;
info = NULL ;
} else {
info = emalloc ( op_live_total * sizeof ( uint32_t ) + ( op_array - > last + 1 ) * sizeof ( uint32_t ) ) ;
for ( i = 0 ; i < op_array - > last ; i + + ) {
op_var_info * opT = opTs [ i ] ;
info [ i ] = info_off ;
while ( opT ) {
info [ info_off + + ] = opT - > var ;
opT = opT - > next ;
}
}
do {
info [ info_off + + ] = opT - > var ;
} while ( ( opT = opT - > next ) ) ;
info [ i ] = info_off ;
}
info [ i ] = info_off ;
zend_arena_release ( & CG ( arena ) , checkpoint ) ;